Skip to content

Commit 23bb952

Browse files
authored
Merge pull request #7949 from NBKelly/trash-like-cards-on-install
offer to trash like cards on install
2 parents 4e65859 + c068051 commit 23bb952

File tree

7 files changed

+109
-32
lines changed

7 files changed

+109
-32
lines changed

src/clj/game/cards/basic.clj

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -61,15 +61,17 @@
6161
(corp-can-pay-and-install?
6262
state side eid
6363
target-card server {:base-cost [(->c :click 1)]
64-
:action :corp-click-install
65-
:no-toast true})
64+
:ignore-ice-cost true
65+
:action :corp-click-install
66+
:no-toast true})
6667
(some
6768
(fn [server]
6869
(corp-can-pay-and-install?
6970
state side eid
7071
target-card server {:base-cost [(->c :click 1)]
71-
:action :corp-click-install
72-
:no-toast true}))
72+
:ignore-ice-cost true
73+
:action :corp-click-install
74+
:no-toast true}))
7375
(installable-servers state target-card))))))
7476
:effect (req (let [{target-card :card server :server} context]
7577
(corp-install

src/clj/game/core/diffs.clj

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -257,6 +257,7 @@
257257
:hand-size
258258
:keep
259259
:quote
260+
:trash-like-cards
260261
:prompt-state
261262
:agenda-point
262263
:agenda-point-req])

src/clj/game/core/installing.clj

Lines changed: 71 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
[game.core.card :refer [agenda? asset? condition-counter? convert-to-condition-counter corp? event? get-card get-zone has-subtype? ice? installed? operation? program? resource? rezzed? upgrade?]]
88
[game.core.card-defs :refer [card-def]]
99
[game.core.cost-fns :refer [ignore-install-cost? install-additional-cost-bonus install-cost]]
10+
[game.core.costs :refer [total-available-credits]]
1011
[game.core.eid :refer [complete-with-result effect-completed make-eid]]
1112
[game.core.engine :refer [checkpoint register-pending-event pay queue-event register-events trigger-event-simult unregister-events]]
1213
[game.core.effects :refer [is-disabled-reg? register-static-abilities unregister-static-abilities update-disabled-cards]]
@@ -27,7 +28,7 @@
2728
[game.core.toasts :refer [toast]]
2829
[game.core.update :refer [update!]]
2930
[game.macros :refer [continue-ability effect req wait-for]]
30-
[game.utils :refer [dissoc-in in-coll? same-card? to-keyword quantify]]
31+
[game.utils :refer [dissoc-in enumerate-str in-coll? same-card? to-keyword quantify]]
3132
[medley.core :refer [find-first]]))
3233

3334
(defn install-locked?
@@ -287,13 +288,14 @@
287288

288289
(defn corp-install-cost
289290
[state side card server
290-
{:keys [base-cost ignore-install-cost ignore-all-cost cost-bonus cached-costs] :as args}]
291+
{:keys [base-cost ignore-install-cost ignore-all-cost cost-bonus cached-costs ignore-ice-cost] :as args}]
291292
(or cached-costs
292293
(let [slot (get-slot state card server args)
293294
dest-zone (get-in @state (cons :corp slot))
294295
ice-cost (if (and (ice? card)
295296
(not ignore-install-cost)
296297
(not ignore-all-cost)
298+
(not ignore-ice-cost)
297299
(not (ignore-install-cost? state side card)))
298300
(count dest-zone)
299301
0)
@@ -316,26 +318,65 @@
316318

317319
(defn- corp-install-pay
318320
"Used by corp-install to pay install costs"
319-
[state side eid card server {:keys [action] :as args}]
321+
[state side eid card server {:keys [action resolved-optional-trash] :as args}]
320322
(let [slot (get-slot state card server args)
321323
costs (corp-install-cost state side card server (dissoc args :cached-costs))
322324
credcost (or (value (find-first #(= :credit (:cost/type %)) costs)) 0)
323325
discount (or (:combined-credit-discount args) 0)
324326
appldisc (if (and (not (zero? credcost)) (not (zero? discount)))
325327
(if (>= credcost discount) discount credcost) 0)
326328
args (if discount (assoc args :cost-bonus (- appldisc discount)) args)
327-
costs (conj costs (->c :credit (- 0 appldisc)))]
328-
;; get a functional discount and apply it to
329-
(if (corp-can-pay-and-install? state side eid card server (assoc args :cached-costs costs))
330-
(wait-for (pay state side (make-eid state (assoc eid :action action)) card costs)
331-
(if-let [payment-str (:msg async-result)]
332-
(if (= server "New remote")
333-
(wait-for (trigger-event-simult state side :server-created nil card)
334-
(make-rid state)
335-
(corp-install-continue state side eid card server args slot payment-str))
336-
(corp-install-continue state side eid card server args slot payment-str))
337-
(effect-completed state side eid)))
338-
(effect-completed state side eid))))
329+
costs (conj costs (->c :credit (- 0 appldisc)))
330+
corp-wants-to-trash? (and (get-in @state [:corp :trash-like-cards])
331+
(seq (get-in @state (concat [:corp] slot)))
332+
(not resolved-optional-trash))]
333+
(if (and (not corp-wants-to-trash?) (corp-can-pay-and-install? state side eid card server (assoc args :cached-costs costs)))
334+
(wait-for
335+
(pay state side (make-eid state (assoc eid :action action)) card costs)
336+
(if-let [payment-str (:msg async-result)]
337+
(if (= server "New remote")
338+
(wait-for (trigger-event-simult state side :server-created nil card)
339+
(make-rid state)
340+
(corp-install-continue state side eid card server args slot payment-str))
341+
(corp-install-continue state side eid card server args slot payment-str))
342+
(effect-completed state side eid)))
343+
;; NOTE - Diwan and Network Exchange both alter the cost of installs
344+
;; if it's not ice AND we can't afford it, there's nothing we can do
345+
;; Diwan will get accounted for, but Network Exchange wont (oh well) - nbk, 2025
346+
(let [shortfall (- (or (value (find-first #(= :credit (:cost/type %)) costs)) 0) (total-available-credits state side eid card))
347+
need-to-trash (max 0 shortfall)
348+
cards-in-slot (count (get-in @state (concat [:corp] slot)))
349+
possible? (and (ice? card) (>= cards-in-slot need-to-trash))]
350+
(cond (and possible? (pos? need-to-trash))
351+
(letfn [(trash-all-or-none [] {:prompt (str "Trash ice protecting " (name-zone :corp slot) " (minimum " need-to-trash ")")
352+
:choices {:req (req (= (:zone target) slot))
353+
:max cards-in-slot}
354+
:waiting-prompt true
355+
:async true
356+
:effect (req (if (>= (count targets) need-to-trash)
357+
(do (system-msg state side (str "trashes " (enumerate-str (map #(card-str state %) targets))))
358+
(wait-for
359+
(trash-cards state side targets {:keep-server-alive true})
360+
(corp-install-pay state side eid card server (assoc args :resolved-optional-trash true))))
361+
(do (toast state :corp (str "You must either trash at least " need-to-trash " ice, or trash none of them"))
362+
(continue-ability state side (trash-all-or-none) card targets))))
363+
:cancel-effect (req (effect-completed state side eid))})]
364+
(continue-ability state side (trash-all-or-none) card nil))
365+
(and corp-wants-to-trash? (zero? need-to-trash))
366+
(continue-ability
367+
state side
368+
{:prompt (str "Trash any number of " (if (ice? card) "ice protecting " "cards in ") (name-zone :corp slot))
369+
:choices {:req (req (= (:zone target) slot))
370+
:max cards-in-slot}
371+
:async true
372+
:waiting-prompt true
373+
:effect (req (do (system-msg state side (str "trashes " (enumerate-str (map #(card-str state %) targets))))
374+
(wait-for
375+
(trash-cards state side targets {:keep-server-alive true})
376+
(corp-install-pay state side eid card server (assoc args :resolved-optional-trash true)))))
377+
:cancel-effect (req (corp-install-pay state side eid card server (assoc args :resolved-optional-trash true)))}
378+
card nil)
379+
:else (effect-completed state side eid))))))
339380

340381
(defn corp-install
341382
"Installs a card in the chosen server. If server is nil, asks for server to install in.
@@ -457,8 +498,7 @@
457498
(defn runner-install-continue
458499
[state side eid card
459500
{:keys [previous-zone host-card facedown no-mu no-msg payment-str] :as args}]
460-
(let [
461-
c (if host-card
501+
(let [c (if host-card
462502
(host state side host-card card)
463503
(move state side card
464504
[:rig (if facedown :facedown (to-keyword (:type card)))]))
@@ -519,17 +559,22 @@
519559
true))))
520560

521561
(defn runner-install-pay
522-
[state side eid card {:keys [no-mu facedown host-card] :as args}]
562+
[state side eid card {:keys [no-mu facedown host-card resolved-optional-trash] :as args}]
523563
(let [costs (runner-install-cost state side (assoc card :facedown facedown) (dissoc args :cached-costs))
524-
available-mem (available-mu state)]
564+
available-mem (available-mu state)
565+
runner-wants-to-trash? (and (get-in @state [:runner :trash-like-cards])
566+
(not resolved-optional-trash))]
525567
(if-not (runner-can-pay-and-install? state side eid card (assoc args :cached-costs costs))
526568
(effect-completed state side eid)
527569
(if (and (program? card)
528570
(not facedown)
529-
(not (or no-mu (sufficient-mu? state card))))
571+
(or (not (or no-mu (sufficient-mu? state card)))
572+
runner-wants-to-trash?))
530573
(continue-ability
531574
state side
532-
{:prompt (format "Insufficient MU to install %s. Trash installed programs?" (:title card))
575+
{:prompt (if (and runner-wants-to-trash? (or no-mu (sufficient-mu? state card)))
576+
(format "Trash installed programs before installing %s?" (:title card))
577+
(format "Insufficient MU to install %s. Trash installed programs?" (:title card)))
533578
:choices {:max (count (filter #(and (program? %) (not (has-ancestor? % host-card))) (all-installed state :runner)))
534579
:card #(and (installed? %)
535580
;; note: rules team says we can't create illegal gamestates by
@@ -541,11 +586,13 @@
541586
:async true
542587
:effect (req (wait-for (trash-cards state side (make-eid state eid) targets {:unpreventable true})
543588
(update-mu state)
544-
(runner-install-pay state side eid card args)))
589+
(runner-install-pay state side eid card (assoc args :resolved-optional-trash true))))
545590
:cancel-effect (req (update-mu state)
546-
(if (= available-mem (available-mu state))
591+
(if (and (= available-mem (available-mu state))
592+
;;(not runner-wants-to-trash?)
593+
(not (or no-mu (sufficient-mu? state card))))
547594
(effect-completed state side eid)
548-
(runner-install-pay state side eid card args)))}
595+
(runner-install-pay state side eid card (assoc args :resolved-optional-trash true))))}
549596
card nil)
550597
(let [played-card (move state side (assoc card :facedown facedown) :play-area {:suppress-event true})]
551598
(wait-for (pay state side (make-eid state eid) card costs)

src/clj/game/core/player.clj

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
set-aside
2121
set-aside-tracking
2222
servers
23+
trash-like-cards
2324
click
2425
click-per-turn
2526
credit
@@ -56,6 +57,7 @@
5657
:credit 5
5758
:bad-publicity (map->BadPublicity {:base 0 :additional 0})
5859
:toast []
60+
:trash-like-cards nil
5961
:hand-size (map->HandSize {:base 5 :total 5})
6062
:agenda-point 0 :agenda-point-req 7
6163
:keep false
@@ -85,6 +87,7 @@
8587
run-credit
8688
link
8789
tag
90+
trash-like-cards
8891
memory
8992
hand-size
9093
agenda-point
@@ -118,6 +121,7 @@
118121
:toast []
119122
:click 0 :click-per-turn 4
120123
:credit 5 :run-credit 0
124+
:trash-like-cards nil
121125
:link 0
122126
:tag (map->Tags {:base 0 :total 0 :is-tagged false})
123127
:memory {:base 4

src/clj/game/core/process_actions.clj

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,13 @@
3232
(handle-end-run state :corp nil)
3333
(fake-checkpoint state)))
3434

35+
(defn set-property
36+
"set properties of the game state that need to be adjustable by the frontend
37+
ie: * do we want an offer to trash like cards on installs?"
38+
[state side {:keys [key value]}]
39+
(case key
40+
:trash-like-cards (swap! state assoc-in [side :trash-like-cards] value)))
41+
3542
(defn command-parser
3643
[state side {:keys [user text] :as args}]
3744
(let [author (or user (get-in @state [side :user]))
@@ -74,6 +81,7 @@
7481
"runner-ability" #'play-runner-ability
7582
"score" #(score %1 %2 (make-eid %1) (get-card %1 (:card %3)) nil)
7683
"select" #'select
84+
"set-property" #'set-property
7785
"shuffle" #'shuffle-deck
7886
"start-turn" #'start-turn
7987
"subroutine" #'play-subroutine

src/cljc/i18n/en.cljc

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -746,6 +746,7 @@
746746
:archives "Archives"
747747
:max-hand "Max hand size"
748748
:brain-damage "Core Damage"
749+
:trash-like-cards "Offer to trash like cards"
749750
:tag-count (fn [[base additional total]] (str base (when (pos? additional) (str " + " additional)) " Tag" (if (not= total 1) "s" "")))
750751
:agenda-count (fn [[agenda-point]] (str agenda-point " Agenda Point" (when (not= agenda-point 1) "s")))
751752
:agenda-point-req (fn [[agenda-point-req]] (if-not (= 7 agenda-point-req) (str " (" agenda-point-req " required)") ""))

src/cljs/nr/gameboard/player_stats.cljs

Lines changed: 18 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,7 @@
6767
(defmethod stats-area "Runner" [runner]
6868
(let [ctrl (stat-controls-for-side :runner)]
6969
(fn [runner]
70-
(let [{:keys [user click credit run-credit memory link tag
70+
(let [{:keys [user click credit run-credit memory link tag trash-like-cards
7171
brain-damage active]} @runner
7272
base-credit (- credit run-credit)
7373
plus-run-credit (when (pos? run-credit) (str "+" run-credit))
@@ -94,12 +94,19 @@
9494
(when show-tagged [:div.warning "!"])]))
9595
(ctrl
9696
:brain-damage
97-
[:div (str brain-damage " " (tr [:game.brain-damage "Core Damage"]))])]))))
97+
[:div (str brain-damage " " (tr [:game.brain-damage "Core Damage"]))])
98+
(when (= (:side @game-state) :runner)
99+
(let [toggle-offer-trash #(send-command "set-property" {:key :trash-like-cards :delta (.. % -target -checked)})]
100+
[:div [:label [:input {:type "checkbox"
101+
:value true
102+
:checked trash-like-cards
103+
:on-click toggle-offer-trash}]
104+
(tr [:game.trash-like-cards "Offer to trash like cards"])]]))]))))
98105

99106
(defmethod stats-area "Corp" [corp]
100107
(let [ctrl (stat-controls-for-side :corp)]
101108
(fn [corp]
102-
(let [{:keys [user click credit bad-publicity active]} @corp
109+
(let [{:keys [user click credit bad-publicity active trash-like-cards]} @corp
103110
icons? (get-in @app-state [:options :player-stats-icons] true)]
104111
[:div.stats-area
105112
(if icons?
@@ -110,7 +117,14 @@
110117
(ctrl :click [:div (tr [:game.click-count] click)])
111118
(ctrl :credit [:div (tr [:game.credit-count] credit -1)])])
112119
(let [{:keys [base additional]} bad-publicity]
113-
(ctrl :bad-publicity [:div (tr [:game.bad-pub-count] base additional)]))]))))
120+
(ctrl :bad-publicity [:div (tr [:game.bad-pub-count] base additional)]))
121+
(when (= (:side @game-state) :corp)
122+
(let [toggle-offer-trash #(send-command "set-property" {:key :trash-like-cards :delta (.. % -target -checked)})]
123+
[:div [:label [:input {:type "checkbox"
124+
:value true
125+
:checked trash-like-cards
126+
:on-click toggle-offer-trash}]
127+
(tr [:game.trash-like-cards "Offer to trash like cards"])]]))]))))
114128

115129
(defn stats-view
116130
[player]

0 commit comments

Comments
 (0)