diff --git a/src/clj/game/cards/agendas.clj b/src/clj/game/cards/agendas.clj index a792e3e71a..0a58a6f191 100644 --- a/src/clj/game/cards/agendas.clj +++ b/src/clj/game/cards/agendas.clj @@ -185,7 +185,7 @@ (resolve-ability state side {:async true :prompt (str "The top cards of R&D are (top->bottom): " - (enumerate-str (map get-title choices))) + (enumerate-cards choices)) :choices ["OK"]} card nil) (continue-ability state side (abt choices) card nil))))}}}})) @@ -258,7 +258,7 @@ :effect (req (continue-ability state side {:prompt (msg "The top cards of R&D are (top->bottom): " - (enumerate-str (map :title (take 5 (:deck corp))))) + (enumerate-cards (take 5 (:deck corp)))) :choices ["OK"] :async true :req (req (not-empty (:deck corp))) @@ -1726,7 +1726,7 @@ :msg (msg "force the Runner to trash " (trash-count-str (:card context)) " and take 1 bad publicity") :async true :effect (req (wait-for (trash-cards state side targets {:cause-card card :cause :forced-to-trash}) - (system-msg state side (str "trashes " (enumerate-str (map :title targets)))) + (system-msg state side (str "trashes " (enumerate-cards targets))) (gain-bad-publicity state :corp eid 1)))}})) (defcard "Project Atlas" @@ -1949,7 +1949,7 @@ (defcard "Reeducation" (letfn [(corp-final [chosen original] - {:prompt (str "The bottom cards of R&D will be " (enumerate-str (map :title chosen))) + {:prompt (str "The bottom cards of R&D will be " (enumerate-cards chosen)) :choices ["Done" "Start over"] :async true :msg (req (let [n (count chosen)] @@ -2499,7 +2499,7 @@ :req (req (same-card? card (:card context))) :msg (msg (if (pos? (count (:deck runner))) (str "trash " - (enumerate-str (map :title (take (adv4? state card) (:deck runner)))) + (enumerate-cards (take (adv4? state card) (:deck runner))) " from the stack") "trash no cards from the stack (it is empty)")) :effect (effect (mill :corp eid :runner (adv4? state card)))}]})) diff --git a/src/clj/game/cards/assets.clj b/src/clj/game/cards/assets.clj index 868ef296b2..18fa017e29 100644 --- a/src/clj/game/cards/assets.clj +++ b/src/clj/game/cards/assets.clj @@ -169,7 +169,7 @@ :prompt (msg "Choose " (quantify (get-counters (get-card state card) :advancement) "program") " to trash") :choices {:max (req (get-counters (get-card state card) :advancement)) :card (every-pred installed? program?)} - :msg (msg "trash " (enumerate-str (map :title targets))) + :msg (msg "trash " (enumerate-cards targets)) :async true :effect (effect (trash-cards eid targets {:cause-card card}))})) @@ -1974,7 +1974,7 @@ (in-discard? %) (not (faceup? %))) :max 2} - :msg (msg "reveal " (enumerate-str (map :title targets)) " from Archives and shuffle them into R&D") + :msg (msg "reveal " (enumerate-cards targets :sorted) " from Archives and shuffle them into R&D") :effect (req (wait-for (reveal state side targets) (doseq [c targets] (move state side c :deck)) @@ -2083,7 +2083,7 @@ :msg (msg "shuffle " (let [seen (filter :seen targets) n (count (filter #(not (:seen %)) targets))] - (str (enumerate-str (map :title seen)) + (str (enumerate-cards seen :sorted) (when (pos? n) (str (when-not (empty? seen) " and ") (quantify n "card"))))) @@ -2168,7 +2168,7 @@ :choices {:card #(and (installed? %) (runner? %)) :max (req (get-counters (get-card state card) :advancement))} - :msg (msg "shuffle " (enumerate-str (map :title targets)) " into the stack") + :msg (msg "shuffle " (enumerate-cards targets) " into the stack") :effect (req (doseq [c targets] (move state :runner c :deck {:shuffled true})) (shuffle! state :runner :deck) @@ -2888,7 +2888,7 @@ :waiting-prompt true :req (req (pos? (get-counters (get-card state card) :advancement))) :prompt (msg "Choose " (quantify (get-counters (get-card state card) :advancement) "piece") " of hardware to trash") - :msg (msg "trash " (enumerate-str (map :title targets))) + :msg (msg "trash " (enumerate-cards targets)) :choices {:max (req (get-counters (get-card state card) :advancement)) :card #(and (installed? %) (hardware? %))} diff --git a/src/clj/game/cards/events.clj b/src/clj/game/cards/events.clj index b94f1ad004..f704627f5e 100644 --- a/src/clj/game/cards/events.clj +++ b/src/clj/game/cards/events.clj @@ -195,7 +195,7 @@ (move state side c :rfg)) (system-msg state side (str "removes " - (enumerate-str (map :title top-5)) + (enumerate-cards top-5) " from the game and draws 5 cards")) (draw state :runner eid 5)))}}) @@ -518,7 +518,7 @@ (defcard "CBI Raid" (letfn [(cbi-final [chosen original] {:player :corp - :prompt (str "The top cards of R&D will be " (enumerate-str (map :title chosen))) + :prompt (str "The top cards of R&D will be " (enumerate-cards chosen)) :choices ["Done" "Start over"] :async true :effect (req (if (= target "Done") @@ -624,7 +624,7 @@ (continue-ability state :runner {:msg "look at the top 3 cards of R&D" - :prompt (msg "The top cards of R&D are (top->bottom): " (enumerate-str (map :title (take 3 (:deck corp))))) + :prompt (msg "The top cards of R&D are (top->bottom): " (enumerate-cards (take 3 (:deck corp)))) :waiting-prompt true :choices ["OK"]} card nil)) @@ -948,13 +948,13 @@ (let [top-8 (sort-by :title (get-set-aside state :corp eid))] (system-msg state side (str "uses " (get-title card) " to set aside " - (enumerate-str (map get-title top-8)) + (enumerate-cards top-8) " from the top of R&D")) (wait-for (resolve-ability state side {:async true :prompt (str "The set aside cards are: " - (enumerate-str (map get-title top-8))) + (enumerate-cards top-8)) :choices ["OK"]} card nil) (wait-for @@ -1075,8 +1075,8 @@ (if (seq installed-cards) (do (system-msg state :runner (str "trashes " (quantify (count installed-cards) "card") - " (" (enumerate-str (map :title installed-cards)) - ") at the end of the run from Diana's Hunt")) + " (" (enumerate-cards installed-cards :sorted) + ") at the end of the run from Diana's Hunt")) (trash-cards state :runner eid installed-cards {:unpreventable true :cause-card card})) (effect-completed state side eid))))}]}) @@ -1194,7 +1194,7 @@ (do (system-msg state side (str "uses " (:title card) " to trash " - (enumerate-str (map :title cards-to-trash)) + (enumerate-cards cards-to-trash :sorted) " from HQ and gain " credits " [Credits]")) (wait-for (trash-cards state :runner (map #(assoc % :seen true) cards-to-trash) {:cause-card card}) @@ -1214,7 +1214,7 @@ (letfn [(ec [trash-cost to-trash] {:async true :prompt "Choose a piece of hardware or program to install" - :msg (msg "trash " (if (empty? to-trash) "no cards" (enumerate-str (map :title to-trash))) + :msg (msg "trash " (if (empty? to-trash) "no cards" (enumerate-cards to-trash :sorted)) " and install " (:title target) " from the Stack, " " lowering the cost by " trash-cost) :choices (req (cancellable (filter #(and (or (program? %) @@ -1334,7 +1334,7 @@ (defcard "Executive Wiretaps" {:on-play - {:msg (msg "reveal " (enumerate-str (sort (map :title (:hand corp)))) " from HQ") + {:msg (msg "reveal " (enumerate-cards (:hand corp) :sorted) " from HQ") :change-in-game-state {:req (req (seq (:hand corp)))} :async true :effect (effect (reveal eid (:hand corp)))}}) @@ -1542,7 +1542,7 @@ (effect (continue-ability (let [top-ten (take 10 (:deck runner))] - {:prompt (str "The top cards of the stack are (top->bottom): " (enumerate-str (map :title top-ten))) + {:prompt (str "The top cards of the stack are (top->bottom): " (enumerate-cards top-ten)) :choices ["OK"] :async true :effect @@ -1562,7 +1562,7 @@ (system-msg state side (str "uses " (get-title card) " to trash " - (enumerate-str (map :title cards)) + (enumerate-cards cards) " from the top of the stack")) (trash-cards state side eid cards {:unpreventable true :cause-card card}))] (if (= target "Done") @@ -1590,7 +1590,7 @@ :card #(and (program? %) (in-hand? %))} :change-in-game-state {:req (req (seq (:hand runner)))} - :msg (msg "trash " (enumerate-str (map :title targets)) " and gain " + :msg (msg "trash " (enumerate-cards targets :sorted) " and gain " (* 2 (count targets)) " [Credits]") :async true :effect (req (wait-for (trash-cards state side targets {:unpreventable true :cause-card card}) @@ -1624,7 +1624,7 @@ :msg (msg "move " (let [seen (filter :seen targets) m (count (remove :seen targets))] - (str (enumerate-str (map :title seen)) + (str (enumerate-cards seen) (when (pos? m) (str (when-not (empty? seen) " and ") (quantify m "unseen card"))) @@ -1836,7 +1836,7 @@ :choices {:max 5 :card #(and (installed? %) (runner? %))} - :msg (msg "trash " (enumerate-str (map :title targets)) + :msg (msg "trash " (enumerate-cards targets) " and draw " (quantify (cards-to-draw targets) "card")) :async true :effect (req (wait-for (trash-cards state side targets {:cause-card card}) @@ -1930,7 +1930,7 @@ (wait-for (trash-cards state side programs {:unpreventable true :cause-card card}) (system-msg state side (str "reveals " - (enumerate-str (map :title programs)) + (enumerate-cards programs) " from the top of the stack," " trashes them, and gains " (count programs) " [Credits]")) @@ -1981,7 +1981,7 @@ (resolve-ability state :corp (reorder-choice :corp (take 4 (:deck corp))) card targets) (let [top-4 (take 4 (get-in @state [:corp :deck]))] (system-msg state :runner (str "reveals " - (enumerate-str (map :title top-4)) + (enumerate-cards top-4) " from the top of R&D (top->bottom)")) (reveal state :runner eid top-4))))}}) @@ -2143,7 +2143,7 @@ (first async-result) " [Credit] and reveals " (if revealed - (str (enumerate-str (map :title revealed)) + (str (enumerate-cards revealed) " from the top of R&D (top->bottom)") "no cards"))) (wait-for @@ -2203,7 +2203,7 @@ top-n-msg (seq (take mill-count (:deck runner)))] (wait-for (mill state :runner :runner mill-count) (system-msg state :runner (if top-n-msg - (str "trashes " (enumerate-str (map :title top-n-msg)) + (str "trashes " (enumerate-cards top-n-msg) " from the top of the stack") "trashes no cards from the top of the stack")) (let [heap-count (min 3 (count (get-in @state [:runner :discard])))] @@ -2220,7 +2220,7 @@ (in-discard? %))} :effect (req (doseq [c targets] (move state side c :deck)) - (system-msg state :runner (str "shuffles " (enumerate-str (map :title targets)) + (system-msg state :runner (str "shuffles " (enumerate-cards targets) " from the heap into the stack, and draws 1 card")) (shuffle! state :runner :deck) (draw state :runner eid 1))} @@ -2443,7 +2443,7 @@ (in-hand? %) (has-subtype? % type))} :prompt (msg "Choose any number of " (decapitalize type) " resources to reveal") - :msg (msg "reveal " (enumerate-str (map :title (sort-by :title targets))) " from the Grip and gain " (count targets) " [Credits]") + :msg (msg "reveal " (enumerate-cards targets :sorted) " from the Grip and gain " (count targets) " [Credits]") :async true :effect (req (wait-for (reveal state side targets) @@ -3247,7 +3247,7 @@ :show-discard true :prompt (str "Choose " (quantify cards-to-move "card") " to add from the heap to the grip") - :msg (msg "add " (enumerate-str (map :title targets)) + :msg (msg "add " (enumerate-cards targets :sorted) " from the heap to the grip") :choices {:max cards-to-move :all true @@ -3842,7 +3842,7 @@ (let [trashed-cards async-result] (system-msg state side (str "uses " (:title card) " to trash " - (enumerate-str (map :title trashed-cards)) + (enumerate-cards trashed-cards) " from the top of the stack")) (continue-ability state side @@ -4084,7 +4084,7 @@ :choices {:req (req (and (corp? target) (in-hand? target))) :max (req (min 2 (count (:hand corp))))} - :msg (msg "shuffle " (enumerate-str (map :title targets)) " into R&D") + :msg (msg "shuffle " (enumerate-cards targets :sorted) " into R&D") :effect (req (doseq [t targets] (move state :corp t :deck)) (shuffle! state :corp :deck))})}}}) diff --git a/src/clj/game/cards/hardware.clj b/src/clj/game/cards/hardware.clj index 007a73b08d..e05158823f 100644 --- a/src/clj/game/cards/hardware.clj +++ b/src/clj/game/cards/hardware.clj @@ -299,7 +299,7 @@ (host state side card (first (:deck runner))))}}} {:event :runner-turn-ends :req (req (seq (:hosted card))) - :msg (msg "trash " (enumerate-str (map :title (:hosted card)))) + :msg (msg "trash " (enumerate-cards (:hosted (get-card state card)) :sorted)) :async true :effect (req (trash-cards state :runner eid (:hosted card)))}]})) @@ -513,7 +513,7 @@ (wait-for (draw state side (count (filter overlap trashed-card-names))) (system-msg state side (str "uses " (:title card) " to trash " - (enumerate-str (map :title trashed-cards)) + (enumerate-cards trashed-cards) " from the grip and draw " (quantify (count async-result) "card"))) (effect-completed state side eid))))))}]}) @@ -1068,8 +1068,8 @@ (defcard "Gachapon" (letfn [(shuffle-end [remove-from-game shuffle-back] - {:msg (msg "shuffle " (enumerate-str (map :title shuffle-back)) " into the stack" - " and remove " (enumerate-str (map :title remove-from-game)) " from the game") + {:msg (msg "shuffle " (enumerate-cards shuffle-back :sorted) " into the stack" + " and remove " (enumerate-cards remove-from-game :sorted) " from the game") :effect (req (doseq [c remove-from-game] (move state side c :rfg)) @@ -1085,14 +1085,14 @@ (empty? set-aside-cards))] {:prompt (msg (if finished? (str "Removing: " (if (not-empty set-aside-cards) - (enumerate-str (map :title set-aside-cards)) + (enumerate-cards set-aside-cards :sorted) "nothing") "[br]Shuffling: " (if (not-empty to-shuffle) - (enumerate-str (map :title to-shuffle)) + (enumerate-cards to-shuffle :sorted) "nothing")) (str "Choose " (- 3 (count to-shuffle)) " more cards to shuffle back." (when (not-empty to-shuffle) - (str "[br]Currently shuffling back: " (enumerate-str (map :title to-shuffle))))))) + (str "[br]Currently shuffling back: " (enumerate-cards to-shuffle :sorted)))))) :async true :not-distinct true ; show cards separately :choices (req (if finished? @@ -1118,12 +1118,12 @@ (let [set-aside-cards (sort-by :title (get-set-aside state side eid))] (system-msg state side (str (:latest-payment-str eid) "to use " (get-title card) " to set aside " - (enumerate-str (map get-title set-aside-cards)) + (enumerate-cards set-aside-cards) " from the top of the stack")) (wait-for (resolve-ability state side {:async true :prompt (str "The set aside cards are: " - (enumerate-str (map get-title set-aside-cards))) + (enumerate-cards set-aside-cards)) :choices ["OK"]} card nil) (continue-ability @@ -1493,7 +1493,7 @@ :action true :choices {:req (req (and (in-hand? target) (program? target) )) :max (req (count (filter program? (:hand runner))))} - :msg (msg "host " (enumerate-str (map :title targets))) + :msg (msg "host " (enumerate-cards targets :sorted)) :effect (req (doseq [t targets] (host state side card t)))} {:cost [(->c :credit 0)] :label "Install a hosted program" @@ -1992,7 +1992,7 @@ {:msg "look at the top 2 cards of the stack" :choices ["OK"] :prompt (msg "The top 2 cards of the stack are " - (enumerate-str (map :title (take 2 (:deck runner)))))}}}] + (enumerate-cards (take 2 (:deck runner))))}}}] :abilities [(set-autoresolve :auto-fire "Prognostic Q-Loop") {:label "Reveal and install top card of the stack" :once :per-turn @@ -2626,7 +2626,7 @@ :all true :card #(and (in-hand? %) (runner? %))} - :msg (msg "trash " (enumerate-str (map :title targets))) + :msg (msg "trash " (enumerate-cards targets :sorted)) :effect (effect (chosen-damage :runner targets))}) card nil)))}]}) diff --git a/src/clj/game/cards/ice.clj b/src/clj/game/cards/ice.clj index 68fdeaa5c9..c214404603 100644 --- a/src/clj/game/cards/ice.clj +++ b/src/clj/game/cards/ice.clj @@ -22,7 +22,7 @@ [game.core.drawing :refer [draw maybe-draw draw-up-to]] [game.core.effects :refer [any-effects get-effects is-disabled? is-disabled-reg? register-lingering-effect unregister-effects-for-card unregister-effect-by-uuid unregister-static-abilities update-disabled-cards]] [game.core.eid :refer [complete-with-result effect-completed make-eid make-result]] - [game.core.engine :refer [gather-events pay register-default-events register-events + [game.core.engine :refer [checkpoint gather-events pay queue-event register-default-events register-events resolve-ability trigger-event trigger-event-simult unregister-events ]] [game.core.events :refer [first-event? run-events]] @@ -785,7 +785,7 @@ :effect (req (system-msg state :corp (str "uses " (:title card) " to trash " - (enumerate-str (map :title (take 3 (:deck runner)))) + (enumerate-cards (take 3 (:deck runner))) " from the top of the stack and trash itself")) (wait-for (mill state :corp (make-eid state eid) :runner 3) @@ -875,7 +875,7 @@ :change-in-game-state {:silent (req true) :req (req (seq (:deck corp)))} :label "Look at the top 5 cards of R&D" :msg "look at the top 5 cards of R&D" - :prompt (msg "The top cards of R&D are (top->bottom) " (enumerate-str (map :title (take 5 (:deck corp))))) + :prompt (msg "The top cards of R&D are (top->bottom) " (enumerate-cards (take 5 (:deck corp)))) :choices ["OK"] :req (req (not-empty (:deck corp))) :effect @@ -1699,7 +1699,7 @@ :label "Reveal the grip" :change-in-game-state {:silent (req true) :req (req (:hand runner))} - :msg (msg "reveal " (enumerate-str (map :title (:hand runner))) " from the grip") + :msg (msg "reveal " (enumerate-cards (:hand runner) :sorted) " from the grip") :effect (effect (reveal eid (:hand runner)))}] {:on-encounter {:prompt "Choose a card type" :choices ["Event" "Hardware" "Program" "Resource"] @@ -2193,28 +2193,14 @@ (in-discard? %)))} :async true :show-discard true - :effect - (req (wait-for - (reveal state side targets) - (doseq [c targets] - (move state :corp c :deck)) - (shuffle! state :corp :deck) - (let [from-hq (map :title (filter in-hand? targets)) - from-archives (map :title (filter in-discard? targets))] - (system-msg - state side - (str "uses " (:title card) " to reveal " - (enumerate-str - (filter identity - [(when (not-empty from-hq) - (str (enumerate-str from-hq) - " from HQ")) - (when (not-empty from-archives) - (str (enumerate-str from-archives) - " from Archives"))])) - ", shuffle them into R&D"))) - (effect-completed state side eid)))} - card nil)))}] + :effect (req (wait-for + (reveal-loud state side card {:and-then "shuffle [them] into R&D"} + targets) + (doseq [c targets] + (move state :corp c :deck)) + (shuffle! state :corp :deck) + (effect-completed state side eid)))} + card nil)))}] {:events [{:event :corp-turn-begins :skippable true :interactive (req true) @@ -2239,11 +2225,12 @@ :choices {:max delta :card #(in-hand? %)} :async true - :effect (req (wait-for (trash-cards state :runner targets) - (system-msg - state :runner - (str "trashes " (enumerate-str (map :title targets)))) - (effect-completed state side eid)))})) + :display-side :runner + :msg (msg "discard " (enumerate-cards targets)) + :effect (req (let [discard (seq (map #(move state side % :discard) targets)) + ev (if (= side :runner) :runner-discard-to-hand-size :corp-discard-to-hand-size)] + (queue-event state ev {:cards discard}) + (checkpoint state nil eid {:durations [ev]})))})) card nil)))}] {:subroutines [sub sub]})) @@ -2815,14 +2802,13 @@ (effect-completed state side eid))))}]}) (defcard "Loot Box" - (letfn [(top-3 [state] (take 3 (get-in @state [:runner :deck]))) - (top-3-names [state] (map :title (top-3 state)))] + (letfn [(top-3 [state] (take 3 (get-in @state [:runner :deck])))] {:subroutines [(end-the-run-unless-runner-pays (->c :credit 2)) {:label "Reveal the top 3 cards of the Stack" :async true :effect (req (if (seq (:deck runner)) (do (system-msg state side (str "uses " (:title card) " to reveal " - (enumerate-str (top-3-names state)) + (enumerate-cards (top-3 state)) " from the top of the stack")) (wait-for (reveal state side (top-3 state)) @@ -3394,7 +3380,7 @@ :msg (msg "add " (let [seen (filter :seen targets) m (count (filter #(not (:seen %)) targets))] - (str (enumerate-str (map :title seen)) + (str (enumerate-cards seen :sorted) (when (pos? m) (str (when-not (empty? seen) " and ") (quantify m "unseen card"))))) @@ -3512,7 +3498,7 @@ :effect (req (let [n (count (filter #(is-type? % target) (:hand runner)))] (system-msg state side (str "uses " (:title card) " to name " target ", reveal " - (enumerate-str (map :title (:hand runner))) + (enumerate-cards (:hand runner) :sorted) " from the grip, and gain " (quantify n "subroutine"))) (wait-for (reveal state side (:hand runner)) @@ -3675,10 +3661,9 @@ :async true :effect (effect (continue-ability - (let [top-cards (take 3 (:deck corp)) - top-names (map :title top-cards)] + (let [top-cards (take 3 (:deck corp))] {:waiting-prompt true - :prompt (str "The top cards of R&D are (top->bottom): " (enumerate-str top-names)) + :prompt (str "The top cards of R&D are (top->bottom): " (enumerate-cards top-cards)) :choices ["Arrange cards" "Shuffle R&D"] :async true :effect @@ -3912,9 +3897,9 @@ (defcard "Slot Machine" (letfn [(top-3 [state] (take 3 (get-in @state [:runner :deck]))) + (top-3-names [cards] (map #(str (:title %) " (" (:type %) ")") cards)) (effect-type [card] (keyword (str "slot-machine-top-3-" (:cid card)))) (name-builder [card] (str (:title card) " (" (:type card) ")")) - (top-3-names [cards] (map name-builder cards)) (top-3-types [state card et] (->> (get-effects state :corp et card) first @@ -3974,9 +3959,7 @@ card nil))}]})) (defcard "Snoop" - {:on-encounter {:msg (msg "reveal " - (enumerate-str (map :title (:hand runner))) - " from the grip") + {:on-encounter {:msg (msg "reveal " (enumerate-cards (:hand runner) :sorted) " from the grip") :async true :effect (effect (reveal eid (:hand runner)))} :abilities [{:req (req (pos? (get-counters card :power))) @@ -4536,7 +4519,7 @@ {:subroutines [(trace-ability 5 {:label "Reveal the grip and trash cards" :msg (msg "reveal " - (enumerate-str (map :title (:hand runner))) + (enumerate-cards (:hand runner) :sorted) " from the grip") :async true :effect (req (wait-for @@ -4544,7 +4527,7 @@ (let [delta (- target (second targets)) cards (filter #(<= (:cost %) delta) (:hand runner))] (system-msg state side (str "uses " (:title card) " to trash " - (enumerate-str (map :title cards)))) + (enumerate-cards cards))) (trash-cards state side eid cards {:cause :subroutine}))))})]}) (defcard "Wall of Static" diff --git a/src/clj/game/cards/identities.clj b/src/clj/game/cards/identities.clj index 87e3e7e91a..a621a9573e 100644 --- a/src/clj/game/cards/identities.clj +++ b/src/clj/game/cards/identities.clj @@ -205,7 +205,7 @@ (continue-ability state side {:async true - :prompt (str "The top of R&D is (top->bottom): " (enumerate-str (map :title top-3)) ". Choose a card to trash") + :prompt (str "The top of R&D is (top->bottom): " (enumerate-cards top-3) ". Choose a card to trash") :not-distinct true :choices (req top-3) :msg (msg (let [target-position (first (positions #{target} (take 3 (:deck corp)))) @@ -612,7 +612,7 @@ (defcard "Chronos Protocol: Haas-Bioroid" {:events [{:event :damage :req (req (= (:damage-type context) :brain)) - :msg (msg "remove all copies of " (enumerate-str (map :title (:cards-trashed context))) ", everywhere, from the game") + :msg (msg "remove all copies of " (enumerate-cards (:cards-trashed context)) ", everywhere, from the game") :effect (req (doseq [c (:cards-trashed context) :let [all-candidates (filter #(and (not (in-rfg? %)) (runner? %) @@ -784,7 +784,7 @@ (wait-for (resolve-ability state side {:async true :waiting-prompt true - :prompt (msg "The top cards of R&D are (top->bottom): " (enumerate-str (map :title top))) + :prompt (msg "The top cards of R&D are (top->bottom): " (enumerate-cards top)) :choices ["OK"]} card nil) (continue-ability @@ -985,12 +985,12 @@ (defcard "Harishchandra Ent.: Where You're the Star" (letfn [(format-grip [runner] (if (pos? (count (:hand runner))) - (enumerate-str (map :title (sort-by :title (:hand runner)))) + (enumerate-cards (:hand runner) :sorted) "no cards"))] {:events [{:event :post-runner-draw :req (req (is-tagged? state)) :msg (msg "see that the Runner drew: " - (enumerate-str (map :title runner-currently-drawing)))} + (enumerate-cards runner-currently-drawing))} {:event :tags-changed :effect (req (if (is-tagged? state) (when-not (get-in @state [:runner :openhand]) @@ -1460,7 +1460,7 @@ (defcard "MaxX: Maximum Punk Rock" (let [ability {:msg (msg (let [deck (:deck runner)] (if (pos? (count deck)) - (str "trash " (enumerate-str (map :title (take 2 deck))) " from the stack and draw 1 card") + (str "trash " (enumerate-cards (take 2 deck)) " from the stack and draw 1 card") "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 @@ -2064,7 +2064,7 @@ :ability (remote-choice %)}) cards)) ev {:prompt (msg "The top of R&D is (in order): " - (enumerate-str (map :title (take 3 (:deck corp))))) + (enumerate-cards (take 3 (:deck corp)))) :async true :msg (msg "look at the top 3 cards of R&D") :effect (req (let [top-3 (take 3 (:deck corp))] @@ -2072,7 +2072,7 @@ state side (choose-one-helper {:prompt (str "The top of R&D is (in order): " - (enumerate-str (map :title top-3))) + (enumerate-cards top-3)) :optional true} (opts-fn top-3)) card nil)))} diff --git a/src/clj/game/cards/operations.clj b/src/clj/game/cards/operations.clj index 941b5c9c97..460b8c4e90 100644 --- a/src/clj/game/cards/operations.clj +++ b/src/clj/game/cards/operations.clj @@ -138,7 +138,7 @@ :choices ["OK"] :async true :effect (req (trash-cards state side eid remaining-cards {:unpreventable true :cause-card card}))})))] - {:prompt (msg "The top cards of R&D are (top->bottom): " (enumerate-str (map :title (take 3 (:deck corp))))) + {:prompt (msg "The top cards of R&D are (top->bottom): " (enumerate-cards (take 3 (:deck corp)))) :change-in-game-state {:req (req (seq (:deck corp)))} :choices ["OK"] :async true @@ -389,7 +389,7 @@ (wait-for (gain-credits state :runner (count trashed-cards)) (system-msg state :runner - (str "trashes " (enumerate-str (map :title trashed-cards)) + (str "trashes " (enumerate-cards trashed-cards) " and gains " (count trashed-cards) " [Credits]")) (effect-completed state side eid)))))} @@ -576,7 +576,7 @@ :card #(and (corp? %) (in-hand? %))} :change-in-game-state {:req (req (seq (:hand corp)))} - :msg (msg "reveal " (enumerate-str (map :title (sort-by :title targets))) " from HQ and gain " (* 2 (count targets)) " [Credits]") + :msg (msg "reveal " (enumerate-cards targets :sorted) " from HQ and gain " (* 2 (count targets)) " [Credits]") :async true :effect (req (wait-for (reveal state side targets) @@ -1090,7 +1090,7 @@ {:on-play {:req (req tagged) :change-in-game-state {:req (req (some resource? (all-installed state :runner)))} - :msg (msg "trash " (enumerate-str (map :title (sort-by :title targets)))) + :msg (msg "trash " (enumerate-cards targets :sorted)) :choices {:max (req (min 2 (count (filter resource? (all-installed state :runner))))) :card #(and (installed? %) (resource? %))} @@ -1174,7 +1174,7 @@ (system-msg state :runner (str (:msg async-result) " to prevent the trashing of " - (enumerate-str (map :title (sort-by :title targets))))) + (enumerate-cards targets :sorted))) (effect-completed state side (make-result eid targets))))} card nil) (let [prevented async-result @@ -1187,7 +1187,7 @@ (str "trashes all " (when (seq prevented) "other ") typemsg - ": " (enumerate-str (map :title (sort-by :title async-result))))) + ": " (enumerate-cards async-result :sorted))) (wait-for (gain-bad-publicity state :corp 1) (when async-result (system-msg state :corp "takes 1 bad publicity from Game Over")) @@ -1292,7 +1292,7 @@ (defcard "Hasty Relocation" (letfn [(hr-final [chosen original] - {:prompt (str "The top cards of R&D will be " (enumerate-str (map :title chosen))) + {:prompt (str "The top cards of R&D will be " (enumerate-cards chosen)) :choices ["Done" "Start over"] :async true :effect (req (if (= target "Done") @@ -1358,7 +1358,7 @@ :all true :card #(and (installed? %) (not (program? %)))} - :msg (msg "trash " (enumerate-str (map :title (sort-by :title targets)))) + :msg (msg "trash " (enumerate-cards targets :sorted)) :async true :effect (effect (trash-cards eid targets {:cause-card card}))} :unsuccessful {:msg "take 1 bad publicity" @@ -1475,7 +1475,7 @@ (system-msg state :corp (str "uses " (:title card) " to reveal " - (enumerate-str (map :title (sort-by :title (:hand runner)))) + (enumerate-cards (:hand runner) :sorted) " from the grip and trash up to " x " resources or events")) (continue-ability state side (iop (dec x)) card nil))))} :unsuccessful {:msg "take 1 bad publicity" @@ -1639,7 +1639,7 @@ :card #(and (rezzed? %) (not (agenda? %)))} :change-in-game-state {:req (req (some rezzed? (all-installed state :corp)))} - :msg (msg "trash " (enumerate-str (map :title targets)) + :msg (msg "trash " (enumerate-cards targets) " and gain " (* (count targets) 3) " [Credits]") :async true :effect (req (wait-for (trash-cards state side targets {:cause-card card}) @@ -1888,7 +1888,7 @@ :choices {:max (req (count (filter #(not (agenda? %)) (all-active-installed state :corp)))) :card #(and (rezzed? %) (not (agenda? %)))} - :msg (msg "trash " (enumerate-str (map :title targets)) + :msg (msg "trash " (enumerate-cards targets :sorted) " and give the runner " (quantify (count targets) "tag")) :async true :effect (req (wait-for (trash-cards state side targets {:cause-card card}) @@ -2292,7 +2292,7 @@ (effect (continue-ability (let [top-five (take 5 (:deck corp))] - {:prompt (str "The top cards of R&D are (top->bottom): " (enumerate-str (map get-title top-five))) + {:prompt (str "The top cards of R&D are (top->bottom): " (enumerate-cards top-five)) :waiting-prompt true :choices ["OK"] :async true @@ -2642,7 +2642,7 @@ :async true :effect (req (system-msg state side (str "uses " (:title card) " to reveal " - (enumerate-str (map :title (sort-by :title (:hand runner)))) + (enumerate-cards (:hand runner) :sorted) " from the grip and trash any copies of " target)) (let [cards (filter #(= target (:title %)) (:hand runner))] (wait-for @@ -2734,7 +2734,7 @@ :choices {:card #(and (installed? %) (runner? %)) :max 2} - :msg (msg (str "move " (enumerate-str (map :title targets)) " to the grip")) + :msg (msg "move " (enumerate-str targets) " to the grip") :effect (req (doseq [c targets] (move state :runner c :hand)))}}) @@ -2911,7 +2911,7 @@ :prompt "Choose one" :choices ["Event" "Hardware" "Program" "Resource"] :msg (msg "name " target - ", reveal " (enumerate-str (map :title (:hand runner))) + ", reveal " (enumerate-cards (:hand runner) :sorted) " from the grip, and gain " (* 2 (count (filter #(is-type? % target) (:hand runner)))) " [Credits]") :async true @@ -3305,7 +3305,7 @@ :effect (req (doseq [t targets] (move state :runner t :deck)) (shuffle! state :runner :deck)) - :msg (msg "shuffle " (enumerate-str (map :title targets)) " into the Stack")}) + :msg (msg "shuffle " (enumerate-cards targets) " into the Stack")}) card nil)))}] {:on-play {:async true :change-in-game-state {:req (req (some #(or (not (rezzed? %)) (can-be-advanced? state %)) (all-installed state :corp)))} diff --git a/src/clj/game/cards/programs.clj b/src/clj/game/cards/programs.clj index 4a25a3d3aa..0de9cf534c 100644 --- a/src/clj/game/cards/programs.clj +++ b/src/clj/game/cards/programs.clj @@ -734,7 +734,7 @@ (system-msg state side (str payment-str " to use " (:title card) " to force the Corp to reveal they drew " - (enumerate-str (map :title cards)))) + (enumerate-cards cards))) (effect-completed state side eid))))))}}}]}) (defcard "Bukhgalter" @@ -1101,7 +1101,7 @@ card nil))))}))] {:on-install {:async true :interactive (req (some #(card-flag? % :runner-install-draw true) (all-active state :runner))) - :msg (msg "reveal " (enumerate-str (map :title (take 5 (:deck runner)))) " from the top of the stack") + :msg (msg "reveal " (enumerate-cards (take 5 (:deck runner))) " from the top of the stack") :waiting-prompt true :effect (req (let [from (take 5 (:deck runner))] (wait-for (reveal state side from) @@ -1361,7 +1361,7 @@ {:target-server :hq :duration :end-of-run :ability - {:msg (msg "reveal " (enumerate-str (map :title (:hand corp))) " from HQ") + {:msg (msg "reveal " (enumerate-cards (:hand corp) :sorted) " from HQ") :async true :effect (effect (reveal eid (:hand corp)))}})] {:abilities [(run-server-ability :hq {:action true @@ -3129,8 +3129,7 @@ {:async true :msg (msg "reveal " (->> (:deck corp) (take 3) - (map :title) - (enumerate-str)) + (enumerate-cards)) " from the top of R&D") :effect (req (wait-for (reveal state side (take 3 (:deck corp))) @@ -3329,7 +3328,7 @@ :all true :card #(and (runner? %) (in-discard? %))} - :msg (msg "shuffle " (enumerate-str (map :title targets)) + :msg (msg "shuffle " (enumerate-cards targets) " into the stack") :effect (req (doseq [c targets] (move state side c :deck)) (shuffle! state side :deck))} diff --git a/src/clj/game/cards/resources.clj b/src/clj/game/cards/resources.clj index 84a0da35f6..e678a5a1d3 100644 --- a/src/clj/game/cards/resources.clj +++ b/src/clj/game/cards/resources.clj @@ -1551,7 +1551,7 @@ (defcard "Find the Truth" {:events [{:event :post-runner-draw :msg (msg "reveal that they drew " - (enumerate-str (map :title runner-currently-drawing))) + (enumerate-cards runner-currently-drawing)) :async true :effect (req (let [current-draws runner-currently-drawing] (reveal state side eid current-draws)))} @@ -2118,7 +2118,7 @@ :change-in-game-state {:req (req (seq (:deck runner)))} :keep-menu-open :while-clicks-left :label "Reveal the top 4 cards of the stack" - :msg (msg "reveal " (enumerate-str (map :title (take 4 (:deck runner)))) " from the top of the stack") + :msg (msg "reveal " (enumerate-cards (take 4 (:deck runner))) " from the top of the stack") :async true :effect (req (let [from (take 4 (:deck runner))] (wait-for (reveal state side from) @@ -2995,7 +2995,7 @@ :on-trash {:async true :effect (req (system-msg state :runner (str "trashes " - (enumerate-str (map :title (take 3 (:deck runner)))) + (enumerate-cards (take 3 (:deck runner))) " from the stack due to " (:title card) " being trashed")) (mill state :runner eid :runner 3))}}) @@ -3346,9 +3346,9 @@ options ["Done"])) :msg (msg (if (= target "Done") - (str "trash " (enumerate-str (map :title set-aside-cards))) + (str "trash " (enumerate-cards set-aside-cards :sorted)) (str "install " (:title target) ", lowering its install cost by 1 [Credits]. " - (enumerate-str (map :title (remove-once #(same-card? % target) set-aside-cards))) + (enumerate-cards (remove-once #(same-card? % target) set-aside-cards) :sorted) " are trashed as a result"))) :effect (req (if (= target "Done") (trash-cards state side (assoc eid :source card) (filter #(not (same-card? % target)) set-aside-cards) {:unpreventable true :cause-card card}) @@ -3514,7 +3514,7 @@ :req (req (and (runner? target) (in-discard? target) (has-trash-ability? target)))} - :msg (msg "shuffle " (enumerate-str (map :title targets)) + :msg (msg "shuffle " (enumerate-cards targets :sorted) " into the stack") :async true :effect (req (doseq [c targets] (move state side c :deck)) diff --git a/src/clj/game/cards/upgrades.clj b/src/clj/game/cards/upgrades.clj index 90764684a5..db4e9e9139 100644 --- a/src/clj/game/cards/upgrades.clj +++ b/src/clj/game/cards/upgrades.clj @@ -246,7 +246,7 @@ :cost [(->c :click 1)] :change-in-game-state {:req (req (pos? (count (:deck corp))))} :async true - :msg (msg (str "reveal " (enumerate-str (map :title (take 3 (:deck corp)))) " from the top of R&D")) + :msg (msg (str "reveal " (enumerate-cards (take 3 (:deck corp))) " from the top of R&D")) :label "Add 1 card from top 3 of R&D to HQ" :waiting-prompt true :effect (req @@ -732,7 +732,7 @@ (protecting-same-server? card (:ice context)))) :msg (msg (let [deck (:deck runner)] (if (pos? (count deck)) - (str "trash " (enumerate-str (map :title (take 2 deck))) " from the stack") + (str "trash " (enumerate-cards (take 2 deck)) " from the stack") "trash no cards from the stack (it is empty)"))) :async true :effect (effect (mill :corp eid :runner 2))}]}) @@ -1958,7 +1958,7 @@ :max n :card #(and (runner? %) (installed? %))} - :msg (msg "force the Runner to trash " (enumerate-str (map :title targets))) + :msg (msg "force the Runner to trash " (enumerate-cards targets)) :effect (req (trash-cards state :runner eid targets {:unpreventable true :cause-card card :cause :forced-to-trash}))}) diff --git a/src/clj/game/core/costs.clj b/src/clj/game/core/costs.clj index 096c4d7f66..f52a951693 100644 --- a/src/clj/game/core/costs.clj +++ b/src/clj/game/core/costs.clj @@ -22,7 +22,7 @@ [game.core.update :refer [update!]] [game.core.virus :refer [number-of-virus-counters]] [game.macros :refer [continue-ability req wait-for]] - [game.utils :refer [enumerate-str quantify same-card?]])) + [game.utils :refer [enumerate-cards enumerate-str quantify same-card?]])) ;; Click (defmethod value :click [cost] (:cost/amount cost)) @@ -343,7 +343,7 @@ (complete-with-result state side eid {:paid/msg (str "forfeits " (quantify (value cost) "agenda") - " (" (enumerate-str (map :title targets)) ")") + " (" (enumerate-cards targets :sorted) ")") :paid/type :forfeit :paid/value (value cost) :paid/targets targets}))} @@ -390,7 +390,7 @@ (complete-with-result state side eid {:paid/msg (str "reveals and trashes " (quantify (count async-result) "card") - " (" (enumerate-str (map :title targets)) ")" + " (" (enumerate-cards targets :sorted) ")" " from " hand) :paid/type :trash-from-hand :paid/value (count async-result) @@ -405,7 +405,7 @@ :suppress-checkpoint true})) (complete-with-result state side eid - {:paid/msg (str "forfeits an agenda (" (enumerate-str (map :title targets)) ")") + {:paid/msg (str "forfeits an agenda (" (enumerate-cards targets :sorted) ")") :paid/type :forfeit :paid/value 1 :paid/targets targets}))}] @@ -919,7 +919,7 @@ state side eid {:paid/msg (str "trashes " (quantify (count async-result) "card") (when (= side :runner) - (str " (" (enumerate-str (map :title async-result)) ")")) + (str " (" (enumerate-cards async-result :sorted) ")")) " randomly from " (if (= :corp side) "HQ" "the grip")) :paid/type :randomly-trash-from-hand @@ -943,7 +943,7 @@ (complete-with-result state side eid {:paid/msg (str "reveals and trashes " (quantify (count async-result) "card") - " (" (enumerate-str (map :title to-trash)) ")" + " (" (enumerate-cards to-trash :sorted) ")" " from " hand) :paid/type :reveal-and-randomly-trash-from-hand :paid/value (count async-result) @@ -964,7 +964,7 @@ (if (= :runner side) "[their] grip" "HQ") (when (and (= :runner side) (pos? (count async-result))) - (str " (" (enumerate-str (map :title async-result)) ")"))) + (str " (" (enumerate-cards async-result :sorted) ")"))) :paid/type :trash-entire-hand :paid/value (count async-result) :paid/targets async-result})))) @@ -990,7 +990,7 @@ state side eid {:paid/msg (str "trashes " (quantify (count async-result) "piece") " of hardware" - " (" (enumerate-str (map :title targets)) ")" + " (" (enumerate-cards targets :sorted) ")" " from [their] grip") :paid/type :trash-hardware-from-hand :paid/value (count async-result) @@ -1017,7 +1017,7 @@ (complete-with-result state side eid {:paid/msg (str "trashes " (quantify (count async-result) "program") - " (" (enumerate-str (map :title targets)) ")" + " (" (enumerate-cards targets :sorted) ")" " from the grip") :paid/type :trash-program-from-hand :paid/value (count async-result) @@ -1044,7 +1044,7 @@ (complete-with-result state side eid {:paid/msg (str "trashes " (quantify (count async-result) "resource") - " (" (enumerate-str (map :title targets)) ")" + " (" (enumerate-cards targets :sorted) ")" " from the grip") :paid/type :trash-resource-from-hand :paid/value (count async-result) @@ -1121,7 +1121,7 @@ (complete-with-result state side eid {:paid/msg (str "shuffles " (quantify (count cards) "card") - " (" (enumerate-str (map :title cards)) ")" + " (" (enumerate-cards cards :sorted) ")" " into " (if (= :corp side) "R&D" "the stack")) :paid/type :shuffle-installed-to-stack :paid/value (count cards) @@ -1224,7 +1224,7 @@ (complete-with-result state side eid {:paid/msg (str "adds " (quantify (count cards) "hosted card") - " to HQ (" (enumerate-str (map :title cards)) ")") + " to HQ (" (enumerate-cards cards :sorted) ")") :paid/type :hosted-to-hq :paid/value (count cards) :paid/targets cards})))} diff --git a/src/clj/game/core/damage.clj b/src/clj/game/core/damage.clj index 14c879236c..fb42258c19 100644 --- a/src/clj/game/core/damage.clj +++ b/src/clj/game/core/damage.clj @@ -10,7 +10,7 @@ [game.core.say :refer [system-msg n-last-logs]] [game.core.winning :refer [flatline]] [game.macros :refer [wait-for]] - [game.utils :refer [dissoc-in enumerate-str side-str]] + [game.utils :refer [dissoc-in enumerate-cards enumerate-str side-str]] [jinteki.utils :refer [str->int]] [taoensso.timbre :as timbre])) @@ -76,7 +76,7 @@ (concat chosen-cards))] (when (= dmg-type :brain) (swap! state update-in [:runner :brain-damage] #(+ % n))) - (when-let [trashed-msg (enumerate-str (map get-title cards-trashed))] + (when-let [trashed-msg (enumerate-cards cards-trashed :sorted)] (system-msg state :runner (str "trashes " trashed-msg " due to " (damage-name dmg-type) " damage")) (swap! state update-in [:stats :corp :damage :all] (fnil + 0) n) (swap! state update-in [:stats :corp :damage dmg-type] (fnil + 0) n) diff --git a/src/clj/game/core/def_helpers.clj b/src/clj/game/core/def_helpers.clj index 38a5b90251..d34dfd5e23 100644 --- a/src/clj/game/core/def_helpers.clj +++ b/src/clj/game/core/def_helpers.clj @@ -26,7 +26,7 @@ [game.core.toasts :refer [toast]] [game.core.tags :refer [gain-tags]] [game.macros :refer [continue-ability effect msg req wait-for]] - [game.utils :refer [enumerate-str remove-once same-card? server-card to-keyword quantify]] + [game.utils :refer [enumerate-cards remove-once same-card? server-card to-keyword quantify]] [jinteki.utils :refer [other-side]])) (defn combine-abilities @@ -84,9 +84,9 @@ ([reorder-side wait-side chosen original dest] {:prompt (if (= dest "bottom") (str "The bottom cards of " (if (= reorder-side :corp) "R&D" "the stack") - " will be " (enumerate-str (map :title (reverse chosen))) ".") + " will be " (enumerate-cards (reverse chosen)) ".") (str "The top cards of " (if (= reorder-side :corp) "R&D" "the stack") - " will be " (enumerate-str (map :title chosen)) ".")) + " will be " (enumerate-cards chosen) ".")) :choices ["Done" "Start over"] :async true :effect (req diff --git a/src/clj/game/core/revealing.clj b/src/clj/game/core/revealing.clj index f2daf20daf..138656fbde 100644 --- a/src/clj/game/core/revealing.clj +++ b/src/clj/game/core/revealing.clj @@ -5,7 +5,7 @@ [game.core.engine :refer [queue-event checkpoint]] [game.core.say :refer [system-msg]] [game.core.servers :refer [name-zone]] - [game.utils :refer [enumerate-str]] + [game.utils :refer [enumerate-str enumerate-cards]] [jinteki.utils :refer [other-side]])) (defn reveal-hand @@ -35,7 +35,7 @@ "Trigger the event for revealing one or more cards, and also handle the log printout" [state side eid card {:keys [forced and-then no-event] :as args} & targets] (let [cards-by-zone (group-by #(select-keys % [:side :zone]) (flatten targets)) - strs (map #(str (enumerate-str (map :title (get cards-by-zone %))) + strs (map #(str (enumerate-cards (get cards-by-zone %) :sorted) " from " (name-zone (:side %) (:zone %))) (keys cards-by-zone)) ;; it's awkward to template a string that could refer to one or many diff --git a/src/clj/game/core/shuffling.clj b/src/clj/game/core/shuffling.clj index 06cec18759..0e61a6f9f7 100644 --- a/src/clj/game/core/shuffling.clj +++ b/src/clj/game/core/shuffling.clj @@ -9,7 +9,7 @@ [game.core.say :refer [system-msg]] [game.core.servers :refer [name-zone]] [game.macros :refer [continue-ability msg req]] - [game.utils :refer [enumerate-str quantify]]) + [game.utils :refer [enumerate-str enumerate-cards quantify]]) (:import [java.security SecureRandom])) @@ -65,7 +65,7 @@ rhs (if (= shuffle-side :corp) "Archives" "the Stack")] (if (seq targets) (let [cards-by-zone (group-by #(select-keys % [:side :zone]) (flatten targets)) - strs (enumerate-str (map #(str (enumerate-str (map :title (get cards-by-zone %))) + strs (enumerate-str (map #(str (enumerate-cards (get cards-by-zone %) :sorted) " from " (name-zone (:side %) (:zone %))) (keys cards-by-zone)))] (doseq [t targets] @@ -96,7 +96,7 @@ :msg (msg "shuffle " (let [seen (filter :seen targets) m (count (filter #(not (:seen %)) targets))] - (str (enumerate-str (map :title seen)) + (str (enumerate-cards seen :sorted) (when (pos? m) (str (when-not (empty? seen) " and ") (quantify m "unseen card"))))) diff --git a/src/clj/game/utils.clj b/src/clj/game/utils.clj index ceb874b261..ea21180ddb 100644 --- a/src/clj/game/utils.clj +++ b/src/clj/game/utils.clj @@ -1,9 +1,10 @@ (ns game.utils (:require - [jinteki.cards :refer [all-cards]] - [clojure.string :as str] - [clj-uuid :as uuid] - [cljc.java-time.instant :as inst])) + [game.core.card :refer [get-title]] + [jinteki.cards :refer [all-cards]] + [clojure.string :as str] + [clj-uuid :as uuid] + [cljc.java-time.instant :as inst])) (defn make-cid [] (uuid/to-string (uuid/v4))) @@ -127,6 +128,15 @@ (str/join (str " " sep " ") strings) (str (apply str (interpose ", " (butlast strings))) (str ", " sep " ") (last strings))))) +(defn enumerate-cards + "Enumerates a collection of cards, optionally in sorted order" + ([cards] (enumerate-cards cards nil)) + ([cards sorted] (enumerate-cards cards sorted "and")) + ([cards sorted sep] + (let [cards (map get-title cards) + cards (if sorted (sort cards) cards)] + (enumerate-str cards sep)))) + (defn in-coll? "true if coll contains elm" [coll elm] diff --git a/test/clj/game/cards/assets_test.clj b/test/clj/game/cards/assets_test.clj index 9c51ad4238..34af08d0f0 100644 --- a/test/clj/game/cards/assets_test.clj +++ b/test/clj/game/cards/assets_test.clj @@ -6599,7 +6599,7 @@ (card-ability state :corp (get-content state :remote1 0) 0) (click-prompt state :corp "Always") (play-and-score state "15 Minutes") - (is (last-log-contains? state "Sure Gamble, Hippo, and Endurance") "Revealed Runner grip") + (is (last-log-contains? state "Endurance, Hippo, and Sure Gamble") "Revealed Runner grip") (is (changed? [(count (:hand (get-runner))) -1] (click-card state :corp "Hippo")) "Hippo was discarded") diff --git a/test/clj/game/cards/ice_test.clj b/test/clj/game/cards/ice_test.clj index 46073f7300..0ac6a11f8a 100644 --- a/test/clj/game/cards/ice_test.clj +++ b/test/clj/game/cards/ice_test.clj @@ -3649,6 +3649,18 @@ (is (no-prompt? state :runner) "No more prompts for the Runner") (is (no-prompt? state :corp) "No more prompts for the Corp")))) +(deftest harvester-actually-is-a-discard + (do-game + (subroutine-test "Harvester" 0 {:runner {:id "Magdalene Keino-Chemutai: Cryptarchitect" + :hand [(qty "Sure Gamble" 4) "Rezeki"] + :deck ["Strike Fund" "Ika" "Steelskin Scarring"]}}) + (is (= 8 (count (:hand (get-runner)))) "Drew 3") + (click-prompts state :runner "Strike Fund" "Rezeki" "Steelskin Scarring") + (is (= 3 (count (:discard (get-runner)))) "Discarded 3") + (click-prompt state :runner "Rezeki") + (is (= "Rezeki" (:title (get-program state 0))) "Rezeki installed from discard") + (is (no-prompt? state :runner) "No prompts for strike fund/steelskin"))) + (deftest herald ;; Herald (do-game