|
7 | 7 | [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?]] |
8 | 8 | [game.core.card-defs :refer [card-def]] |
9 | 9 | [game.core.cost-fns :refer [ignore-install-cost? install-additional-cost-bonus install-cost]] |
| 10 | + [game.core.costs :refer [total-available-credits]] |
10 | 11 | [game.core.eid :refer [complete-with-result effect-completed make-eid]] |
11 | 12 | [game.core.engine :refer [checkpoint register-pending-event pay queue-event register-events trigger-event-simult unregister-events]] |
12 | 13 | [game.core.effects :refer [is-disabled-reg? register-static-abilities unregister-static-abilities update-disabled-cards]] |
|
27 | 28 | [game.core.toasts :refer [toast]] |
28 | 29 | [game.core.update :refer [update!]] |
29 | 30 | [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]] |
31 | 32 | [medley.core :refer [find-first]])) |
32 | 33 |
|
33 | 34 | (defn install-locked? |
|
287 | 288 |
|
288 | 289 | (defn corp-install-cost |
289 | 290 | [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}] |
291 | 292 | (or cached-costs |
292 | 293 | (let [slot (get-slot state card server args) |
293 | 294 | dest-zone (get-in @state (cons :corp slot)) |
294 | 295 | ice-cost (if (and (ice? card) |
295 | 296 | (not ignore-install-cost) |
296 | 297 | (not ignore-all-cost) |
| 298 | + (not ignore-ice-cost) |
297 | 299 | (not (ignore-install-cost? state side card))) |
298 | 300 | (count dest-zone) |
299 | 301 | 0) |
|
316 | 318 |
|
317 | 319 | (defn- corp-install-pay |
318 | 320 | "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}] |
320 | 322 | (let [slot (get-slot state card server args) |
321 | 323 | costs (corp-install-cost state side card server (dissoc args :cached-costs)) |
322 | 324 | credcost (or (value (find-first #(= :credit (:cost/type %)) costs)) 0) |
323 | 325 | discount (or (:combined-credit-discount args) 0) |
324 | 326 | appldisc (if (and (not (zero? credcost)) (not (zero? discount))) |
325 | 327 | (if (>= credcost discount) discount credcost) 0) |
326 | 328 | 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)))))) |
339 | 380 |
|
340 | 381 | (defn corp-install |
341 | 382 | "Installs a card in the chosen server. If server is nil, asks for server to install in. |
|
457 | 498 | (defn runner-install-continue |
458 | 499 | [state side eid card |
459 | 500 | {: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 |
462 | 502 | (host state side host-card card) |
463 | 503 | (move state side card |
464 | 504 | [:rig (if facedown :facedown (to-keyword (:type card)))])) |
|
519 | 559 | true)))) |
520 | 560 |
|
521 | 561 | (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}] |
523 | 563 | (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))] |
525 | 567 | (if-not (runner-can-pay-and-install? state side eid card (assoc args :cached-costs costs)) |
526 | 568 | (effect-completed state side eid) |
527 | 569 | (if (and (program? card) |
528 | 570 | (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?)) |
530 | 573 | (continue-ability |
531 | 574 | 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))) |
533 | 578 | :choices {:max (count (filter #(and (program? %) (not (has-ancestor? % host-card))) (all-installed state :runner))) |
534 | 579 | :card #(and (installed? %) |
535 | 580 | ;; note: rules team says we can't create illegal gamestates by |
|
541 | 586 | :async true |
542 | 587 | :effect (req (wait-for (trash-cards state side (make-eid state eid) targets {:unpreventable true}) |
543 | 588 | (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)))) |
545 | 590 | :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)))) |
547 | 594 | (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))))} |
549 | 596 | card nil) |
550 | 597 | (let [played-card (move state side (assoc card :facedown facedown) :play-area {:suppress-event true})] |
551 | 598 | (wait-for (pay state side (make-eid state eid) card costs) |
|
0 commit comments