From ad48e61462c73ea2e2538bed97c4ec711bc8d875 Mon Sep 17 00:00:00 2001 From: Oguz Han Asnaz Date: Wed, 24 Jul 2019 23:24:42 +0200 Subject: [PATCH] MVP for breaking all subroutines --- src/clj/game/cards/programs.clj | 86 +++++++++++++++++++---------- src/clj/game/core/actions.clj | 51 ++++++++++++++++- test/clj/game_test/engine/costs.clj | 51 +++++++++++++++++ 3 files changed, 158 insertions(+), 30 deletions(-) diff --git a/src/clj/game/cards/programs.clj b/src/clj/game/cards/programs.clj index fa5a724b6b..7358dd1c8a 100644 --- a/src/clj/game/cards/programs.clj +++ b/src/clj/game/cards/programs.clj @@ -74,8 +74,11 @@ (or (= "ICE" subtype) (has-subtype? current-ice subtype)) true))) - :label (str (build-cost-str (if (number? cost) [:credit cost] cost)) - ": break " + :break n + :cost (if (number? cost) + [:credit cost] + cost) + :label (str "break " (when (> n 1) "up to ") (if (pos? n) n "any number of") (when subtype (str " " subtype)) @@ -90,32 +93,55 @@ auto-pump routine in core, IF we are encountering a rezzed ice with a subtype we can break." {:effect - (req (let [abs (filter #(not= (:dynamic %) :auto-pump) (:abilities card)) - pumpabi (some #(when (:pump %) %) abs) - pumpcst (when pumpabi (second (drop-while #(and (not= % :credit) - (not= % "credit")) - (:cost pumpabi)))) - current-ice (when-not (get-in @state [:run :ending]) (get-card state current-ice)) - strdif (when (and (get-strength current-ice) - (get-strength card)) - (max 0 (- (get-strength current-ice) - (get-strength card)))) - pumpnum (when (and strdif - (:pump pumpabi)) - (int (Math/ceil (/ strdif (:pump pumpabi)))))] - (update! state side - (assoc card :abilities - (if (and pumpcst - pumpnum - (rezzed? current-ice) - (or (some #(has-subtype? current-ice %) (:breaks card)) - (= (first (:breaks card)) "All")) - (pos? pumpnum)) - (vec (cons {:dynamic :auto-pump - :cost [:credit (* pumpcst pumpnum)] - :label (str "Match strength of " (:title current-ice))} - abs)) - abs)))))}) + (req (doall (let [abs (filter #(and (not= (:dynamic %) :auto-pump) + (not= (:dynamic %) :auto-pump-and-break)) + (:abilities (card-def card))) + pumpabi (some #(when (:pump %) %) abs) + pumpcst (when pumpabi (second (drop-while #(and (not= % :credit) + (not= % "credit")) + (:cost pumpabi)))) + current-ice (when-not (get-in @state [:run :ending]) (get-card state current-ice)) + strdif (when current-ice (max 0 (- (or (:current-strength current-ice) (:strength current-ice)) + (or (:current-strength card) (:strength card))))) + pumpnum (when strdif (int (Math/ceil (/ strdif (:pump pumpabi 1))))) + total-pump-cost (when (and pumpnum pumpabi) + (merge-costs (repeat pumpnum (:cost pumpabi)))) + breakabi (some #(when (:break %) %) abs) + nsubs (when (:subroutines current-ice) + (count (remove :broken (:subroutines current-ice)))) + some-already-broken (not= (:subroutines current-ice) + (remove :broken (:subroutines current-ice))) + subs-broken-at-once (when breakabi (:break breakabi)) + times-break (when (and nsubs subs-broken-at-once) + (if (pos? subs-broken-at-once) + (/ nsubs subs-broken-at-once) + 1)) + total-break-cost (when (and times-break breakabi) + (repeat times-break (:cost breakabi))) + total-cost (merge-costs (conj total-pump-cost total-break-cost))] + (update! state side + (assoc card :abilities + (if (and pumpcst + pumpnum + (rezzed? current-ice) + (or (some #(has-subtype? current-ice %) (:breaks card)) + (= (first (:breaks card)) "All"))) + (vec (concat (when (and (pos? nsubs) + (can-pay? state side eid card total-cost)) + [{:dynamic :auto-pump-and-break + :cost (first total-cost) ; ToDo: this should be refactored at some point to take the whole total-cost seq + ; Since cost deduction is done in play-auto-pump-and-break, this isn't so important + :label (str (if (pos? pumpnum) + "Match strength and fully break " + "Fully break ") + (:title current-ice))}]) + (when (and (pos? pumpnum) + (can-pay? state side eid card total-pump-cost)) + [{:dynamic :auto-pump + :cost (first total-pump-cost) + :label (str "Match strength of " (:title current-ice))}]) + abs)) + abs))))))}) ;; Takes a vector of ice subtypes that can be broken (or ["All"] for ;; AI breakers) and a card definition, and returns a new card definition that @@ -195,7 +221,9 @@ " for the remainder of the run" (= duration :all-turn) " for the remainder of the turn")) - :cost [:credit cost] + :cost (if (number? cost) + [:credit cost] + cost) :effect (effect (pump card strength duration)) :pump strength})) diff --git a/src/clj/game/core/actions.clj b/src/clj/game/core/actions.clj index 744d2c5c5b..bf41d1729f 100644 --- a/src/clj/game/core/actions.clj +++ b/src/clj/game/core/actions.clj @@ -7,7 +7,7 @@ name-zone play-instant purge make-run runner-install trash update-breaker-strength update-ice-in-server update-run-ice win can-run? can-run-server? can-score? say play-sfx base-mod-size free-mu - reset-all-subs! resolve-subroutine! resolve-unbroken-subs!) + reset-all-subs! resolve-subroutine! resolve-unbroken-subs! break-all-subroutines!) ;;; Neutral actions (defn play @@ -337,6 +337,54 @@ "the strength of " (:title card) " to " (:current-strength (get-card state card)))))))) +(defn play-auto-pump-and-break + "Use play-auto-pump and then break all available subroutines" + [state side args] + (let [run (:run @state) + card (get-card state (:card args)) + eid (make-eid state {:source card :source-type :ability}) + run-ice (get-run-ices state) + ice-cnt (count run-ice) + ice-idx (dec (:position run 0)) + in-range (and (pos? ice-cnt) (< -1 ice-idx ice-cnt)) + current-ice (when (and run in-range) (get-card state (run-ice ice-idx))) + pumpabi (some #(when (:pump %) %) (:abilities (card-def card))) + strdif (when current-ice (max 0 (- (or (:current-strength current-ice) (:strength current-ice)) + (or (:current-strength card) (:strength card))))) + pumpnum (when strdif (int (Math/ceil (/ strdif (:pump pumpabi 1))))) + total-pump-cost (merge-costs (repeat pumpnum (:cost pumpabi))) + breakabi (some #(when (:break %) %) (:abilities (card-def card))) + nsubs (when (:subroutines current-ice) + (count (remove :broken (:subroutines current-ice)))) + some-already-broken (not= (:subroutines current-ice) + (remove :broken (:subroutines current-ice))) + subs-broken-at-once (when breakabi (:break breakabi)) + times-break (when (and nsubs subs-broken-at-once) + (if (pos? subs-broken-at-once) + (/ nsubs subs-broken-at-once) + 1)) + total-break-cost (when (and times-break breakabi) + (repeat times-break (:cost breakabi))) + total-cost (merge-costs (conj total-pump-cost total-break-cost))] + (when (can-pay? state side eid card (:title card) total-cost) + (wait-for (pay-sync state side (make-eid state eid) card total-cost) + (dotimes [n pumpnum] (resolve-ability state side (dissoc pumpabi :cost :msg) (get-card state card) nil)) + (break-all-subroutines! state current-ice) + (system-msg state side (if (pos? pumpnum) + (str (build-spend-msg async-result "increase") + "the strength of " (:title card) " to " + (:current-strength (get-card state card)) + " and break all " nsubs " subroutines on " + (:title current-ice)) + (str (build-spend-msg async-result "use") + (:title card) + " to break " + (if some-already-broken + "the remaining " + "all ") + nsubs " subroutines on " + (:title current-ice)))))))) + (defn play-copy-ability "Play an ability from another card's definition." [state side {:keys [card source index] :as args}] @@ -349,6 +397,7 @@ (def dynamic-abilities {"auto-pump" play-auto-pump + "auto-pump-and-break" play-auto-pump-and-break "copy" play-copy-ability}) (defn play-dynamic-ability diff --git a/test/clj/game_test/engine/costs.clj b/test/clj/game_test/engine/costs.clj index 57a37b58fa..9e786983a7 100644 --- a/test/clj/game_test/engine/costs.clj +++ b/test/clj/game_test/engine/costs.clj @@ -96,3 +96,54 @@ (click-prompt state :runner "Place 1 [Credits]") (is (= 5 (:current-strength (refresh cor))) "Corroder is at 5 strength") (is (= (- cre 2) (:credit (get-runner))) "Spent 2 (+1 from Cloak) to pump"))))) + +(deftest pump-and-break + (testing "Basic test" + (do-game + (new-game {:runner {:hand ["Corroder"]} + :corp {:hand ["Hive"]}}) + (play-from-hand state :corp "Hive" "HQ") + (take-credits state :corp) + (core/gain state :runner :credit 10) + (play-from-hand state :runner "Corroder") + (run-on state :hq) + (let [cor (get-program state 0) + hive (get-ice state :hq 0)] + (is (= 2 (:current-strength (refresh cor))) "Corroder starts at 2 strength") + (core/play-dynamic-ability state :runner {:dynamic "auto-pump-and-break" :card (refresh cor)}) + (is (= 3 (:current-strength (refresh cor))) "Corroder now at 3 strength") + (is (empty? (remove :broken (:subroutines (refresh hive)))) "Hive is now fully broken")))) + (testing "Auto-pump first" + (do-game + (new-game {:runner {:hand ["Corroder"]} + :corp {:hand ["Hive"]}}) + (play-from-hand state :corp "Hive" "HQ") + (take-credits state :corp) + (core/gain state :runner :credit 10) + (play-from-hand state :runner "Corroder") + (run-on state :hq) + (let [cor (get-program state 0) + hive (get-ice state :hq 0)] + (core/play-dynamic-ability state :runner {:dynamic "auto-pump" :card (refresh cor)}) + (is (= 3 (:current-strength (refresh cor))) "Corroder now at 3 strength") + (core/play-dynamic-ability state :runner {:dynamic "auto-pump-and-break" :card (refresh cor)}) + (is (empty? (remove :broken (:subroutines (refresh hive)))) "Hive is now fully broken")))) + (testing "Auto-pump and break some subs manually first" + (do-game + (new-game {:runner {:hand ["Corroder"]} + :corp {:hand ["Hive"]}}) + (play-from-hand state :corp "Hive" "HQ") + (take-credits state :corp) + (core/gain state :runner :credit 10) + (play-from-hand state :runner "Corroder") + (run-on state :hq) + (let [cor (get-program state 0) + hive (get-ice state :hq 0)] + (core/play-dynamic-ability state :runner {:dynamic "auto-pump" :card (refresh cor)}) + (is (= 3 (:current-strength (refresh cor))) "Corroder is now at 3 strength") + (card-ability state :runner (refresh cor) 0) + (click-prompt state :runner "End the run") + (click-prompt state :runner "Done") + (is (= 4 (count (remove :broken (:subroutines (refresh hive))))) "Only broken 1 sub") + (core/play-dynamic-ability state :runner {:dynamic "auto-pump-and-break" :card (refresh cor)}) + (is (empty? (remove :broken (:subroutines (refresh hive)))) "Hive is now fully broken")))))