Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 6 additions & 4 deletions src/clj/game/cards/basic.clj
Original file line number Diff line number Diff line change
Expand Up @@ -61,15 +61,17 @@
(corp-can-pay-and-install?
state side eid
target-card server {:base-cost [(->c :click 1)]
:action :corp-click-install
:no-toast true})
:ignore-ice-cost true
:action :corp-click-install
:no-toast true})
(some
(fn [server]
(corp-can-pay-and-install?
state side eid
target-card server {:base-cost [(->c :click 1)]
:action :corp-click-install
:no-toast true}))
:ignore-ice-cost true
:action :corp-click-install
:no-toast true}))
(installable-servers state target-card))))))
:effect (req (let [{target-card :card server :server} context]
(corp-install
Expand Down
1 change: 1 addition & 0 deletions src/clj/game/core/diffs.clj
Original file line number Diff line number Diff line change
Expand Up @@ -256,6 +256,7 @@
:hand-size
:keep
:quote
:trash-like-cards
:prompt-state
:agenda-point
:agenda-point-req])
Expand Down
95 changes: 71 additions & 24 deletions src/clj/game/core/installing.clj
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +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?]]
[game.core.card-defs :refer [card-def]]
[game.core.cost-fns :refer [ignore-install-cost? install-additional-cost-bonus install-cost]]
[game.core.costs :refer [total-available-credits]]
[game.core.eid :refer [complete-with-result effect-completed make-eid]]
[game.core.engine :refer [checkpoint register-pending-event pay queue-event register-events trigger-event-simult unregister-events]]
[game.core.effects :refer [is-disabled-reg? register-static-abilities unregister-static-abilities update-disabled-cards]]
Expand All @@ -27,7 +28,7 @@
[game.core.toasts :refer [toast]]
[game.core.update :refer [update!]]
[game.macros :refer [continue-ability effect req wait-for]]
[game.utils :refer [dissoc-in in-coll? same-card? to-keyword quantify]]
[game.utils :refer [dissoc-in enumerate-str in-coll? same-card? to-keyword quantify]]
[medley.core :refer [find-first]]))

(defn install-locked?
Expand Down Expand Up @@ -287,13 +288,14 @@

(defn corp-install-cost
[state side card server
{:keys [base-cost ignore-install-cost ignore-all-cost cost-bonus cached-costs] :as args}]
{:keys [base-cost ignore-install-cost ignore-all-cost cost-bonus cached-costs ignore-ice-cost] :as args}]
(or cached-costs
(let [slot (get-slot state card server args)
dest-zone (get-in @state (cons :corp slot))
ice-cost (if (and (ice? card)
(not ignore-install-cost)
(not ignore-all-cost)
(not ignore-ice-cost)
(not (ignore-install-cost? state side card)))
(count dest-zone)
0)
Expand All @@ -316,26 +318,65 @@

(defn- corp-install-pay
"Used by corp-install to pay install costs"
[state side eid card server {:keys [action] :as args}]
[state side eid card server {:keys [action resolved-optional-trash] :as args}]
(let [slot (get-slot state card server args)
costs (corp-install-cost state side card server (dissoc args :cached-costs))
credcost (or (value (find-first #(= :credit (:cost/type %)) costs)) 0)
discount (or (:combined-credit-discount args) 0)
appldisc (if (and (not (zero? credcost)) (not (zero? discount)))
(if (>= credcost discount) discount credcost) 0)
args (if discount (assoc args :cost-bonus (- appldisc discount)) args)
costs (conj costs (->c :credit (- 0 appldisc)))]
;; get a functional discount and apply it to
(if (corp-can-pay-and-install? state side eid card server (assoc args :cached-costs costs))
(wait-for (pay state side (make-eid state (assoc eid :action action)) card costs)
(if-let [payment-str (:msg async-result)]
(if (= server "New remote")
(wait-for (trigger-event-simult state side :server-created nil card)
(make-rid state)
(corp-install-continue state side eid card server args slot payment-str))
(corp-install-continue state side eid card server args slot payment-str))
(effect-completed state side eid)))
(effect-completed state side eid))))
costs (conj costs (->c :credit (- 0 appldisc)))
corp-wants-to-trash? (and (get-in @state [:corp :trash-like-cards])
(seq (get-in @state (concat [:corp] slot)))
(not resolved-optional-trash))]
(if (and (not corp-wants-to-trash?) (corp-can-pay-and-install? state side eid card server (assoc args :cached-costs costs)))
(wait-for
(pay state side (make-eid state (assoc eid :action action)) card costs)
(if-let [payment-str (:msg async-result)]
(if (= server "New remote")
(wait-for (trigger-event-simult state side :server-created nil card)
(make-rid state)
(corp-install-continue state side eid card server args slot payment-str))
(corp-install-continue state side eid card server args slot payment-str))
(effect-completed state side eid)))
;; NOTE - Diwan and Network Exchange both alter the cost of installs
;; if it's not ice AND we can't afford it, there's nothing we can do
;; Diwan will get accounted for, but Network Exchange wont (oh well) - nbk, 2025
(let [shortfall (- (or (value (find-first #(= :credit (:cost/type %)) costs)) 0) (total-available-credits state side eid card))
need-to-trash (max 0 shortfall)
cards-in-slot (count (get-in @state (concat [:corp] slot)))
possible? (and (ice? card) (>= cards-in-slot need-to-trash))]
(cond (and possible? (pos? need-to-trash))
(letfn [(trash-all-or-none [] {:prompt (str "Trash ice protecting " (name-zone :corp slot) " (minimum " need-to-trash ")")
:choices {:req (req (= (:zone target) slot))
:max cards-in-slot}
:waiting-prompt true
:async true
:effect (req (if (>= (count targets) need-to-trash)
(do (system-msg state side (str "trashes " (enumerate-str (map #(card-str state %) targets))))
(wait-for
(trash-cards state side targets {:keep-server-alive true})
(corp-install-pay state side eid card server (assoc args :resolved-optional-trash true))))
(do (toast state :corp (str "You must either trash at least " need-to-trash " ice, or trash none of them"))
(continue-ability state side (trash-all-or-none) card targets))))
:cancel-effect (req (effect-completed state side eid))})]
(continue-ability state side (trash-all-or-none) card nil))
(and corp-wants-to-trash? (zero? need-to-trash))
(continue-ability
state side
{:prompt (str "Trash any number of " (if (ice? card) "ice protecting " "cards in ") (name-zone :corp slot))
:choices {:req (req (= (:zone target) slot))
:max cards-in-slot}
:async true
:waiting-prompt true
:effect (req (do (system-msg state side (str "trashes " (enumerate-str (map #(card-str state %) targets))))
(wait-for
(trash-cards state side targets {:keep-server-alive true})
(corp-install-pay state side eid card server (assoc args :resolved-optional-trash true)))))
:cancel-effect (req (corp-install-pay state side eid card server (assoc args :resolved-optional-trash true)))}
card nil)
:else (effect-completed state side eid))))))

(defn corp-install
"Installs a card in the chosen server. If server is nil, asks for server to install in.
Expand Down Expand Up @@ -457,8 +498,7 @@
(defn runner-install-continue
[state side eid card
{:keys [previous-zone host-card facedown no-mu no-msg payment-str] :as args}]
(let [
c (if host-card
(let [c (if host-card
(host state side host-card card)
(move state side card
[:rig (if facedown :facedown (to-keyword (:type card)))]))
Expand Down Expand Up @@ -519,17 +559,22 @@
true))))

(defn runner-install-pay
[state side eid card {:keys [no-mu facedown host-card] :as args}]
[state side eid card {:keys [no-mu facedown host-card resolved-optional-trash] :as args}]
(let [costs (runner-install-cost state side (assoc card :facedown facedown) (dissoc args :cached-costs))
available-mem (available-mu state)]
available-mem (available-mu state)
runner-wants-to-trash? (and (get-in @state [:runner :trash-like-cards])
(not resolved-optional-trash))]
(if-not (runner-can-pay-and-install? state side eid card (assoc args :cached-costs costs))
(effect-completed state side eid)
(if (and (program? card)
(not facedown)
(not (or no-mu (sufficient-mu? state card))))
(or (not (or no-mu (sufficient-mu? state card)))
runner-wants-to-trash?))
(continue-ability
state side
{:prompt (format "Insufficient MU to install %s. Trash installed programs?" (:title card))
{:prompt (if (and runner-wants-to-trash? (or no-mu (sufficient-mu? state card)))
(format "Trash installed programs before installing %s?" (:title card))
(format "Insufficient MU to install %s. Trash installed programs?" (:title card)))
:choices {:max (count (filter #(and (program? %) (not (has-ancestor? % host-card))) (all-installed state :runner)))
:card #(and (installed? %)
;; note: rules team says we can't create illegal gamestates by
Expand All @@ -541,11 +586,13 @@
:async true
:effect (req (wait-for (trash-cards state side (make-eid state eid) targets {:unpreventable true})
(update-mu state)
(runner-install-pay state side eid card args)))
(runner-install-pay state side eid card (assoc args :resolved-optional-trash true))))
:cancel-effect (req (update-mu state)
(if (= available-mem (available-mu state))
(if (and (= available-mem (available-mu state))
;;(not runner-wants-to-trash?)
(not (or no-mu (sufficient-mu? state card))))
(effect-completed state side eid)
(runner-install-pay state side eid card args)))}
(runner-install-pay state side eid card (assoc args :resolved-optional-trash true))))}
card nil)
(let [played-card (move state side (assoc card :facedown facedown) :play-area {:suppress-event true})]
(wait-for (pay state side (make-eid state eid) card costs)
Expand Down
4 changes: 4 additions & 0 deletions src/clj/game/core/player.clj
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
set-aside
set-aside-tracking
servers
trash-like-cards
click
click-per-turn
credit
Expand Down Expand Up @@ -56,6 +57,7 @@
:credit 5
:bad-publicity (map->BadPublicity {:base 0 :additional 0})
:toast []
:trash-like-cards nil
:hand-size (map->HandSize {:base 5 :total 5})
:agenda-point 0 :agenda-point-req 7
:keep false
Expand Down Expand Up @@ -85,6 +87,7 @@
run-credit
link
tag
trash-like-cards
memory
hand-size
agenda-point
Expand Down Expand Up @@ -118,6 +121,7 @@
:toast []
:click 0 :click-per-turn 4
:credit 5 :run-credit 0
:trash-like-cards nil
:link 0
:tag (map->Tags {:base 0 :total 0 :is-tagged false})
:memory {:base 4
Expand Down
8 changes: 8 additions & 0 deletions src/clj/game/core/process_actions.clj
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,13 @@
(handle-end-run state :corp nil)
(fake-checkpoint state)))

(defn set-property
"set properties of the game state that need to be adjustable by the frontend
ie: * do we want an offer to trash like cards on installs?"
[state side {:keys [key value]}]
(case key
:trash-like-cards (swap! state assoc-in [side :trash-like-cards] value)))

(defn command-parser
[state side {:keys [user text] :as args}]
(let [author (or user (get-in @state [side :user]))
Expand Down Expand Up @@ -74,6 +81,7 @@
"runner-ability" #'play-runner-ability
"score" #(score %1 %2 (make-eid %1) (get-card %1 (:card %3)) nil)
"select" #'select
"set-property" #'set-property
"shuffle" #'shuffle-deck
"start-turn" #'start-turn
"subroutine" #'play-subroutine
Expand Down
1 change: 1 addition & 0 deletions src/cljc/i18n/en.cljc
Original file line number Diff line number Diff line change
Expand Up @@ -745,6 +745,7 @@
:archives "Archives"
:max-hand "Max hand size"
:brain-damage "Core Damage"
:trash-like-cards "Offer to trash like cards"
:tag-count (fn [[base additional total]] (str base (when (pos? additional) (str " + " additional)) " Tag" (if (not= total 1) "s" "")))
:agenda-count (fn [[agenda-point]] (str agenda-point " Agenda Point" (when (not= agenda-point 1) "s")))
:link-strength "Link Strength"
Expand Down
22 changes: 18 additions & 4 deletions src/cljs/nr/gameboard/player_stats.cljs
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@
(defmethod stats-area "Runner" [runner]
(let [ctrl (stat-controls-for-side :runner)]
(fn [runner]
(let [{:keys [user click credit run-credit memory link tag
(let [{:keys [user click credit run-credit memory link tag trash-like-cards
brain-damage active]} @runner
base-credit (- credit run-credit)
plus-run-credit (when (pos? run-credit) (str "+" run-credit))
Expand All @@ -94,12 +94,19 @@
(when show-tagged [:div.warning "!"])]))
(ctrl
:brain-damage
[:div (str brain-damage " " (tr [:game.brain-damage "Core Damage"]))])]))))
[:div (str brain-damage " " (tr [:game.brain-damage "Core Damage"]))])
(when (= (:side @game-state) :runner)
(let [toggle-offer-trash #(send-command "set-property" {:key :trash-like-cards :delta (.. % -target -checked)})]
[:div [:label [:input {:type "checkbox"
:value true
:checked trash-like-cards
:on-click toggle-offer-trash}]
(tr [:game.trash-like-cards "Offer to trash like cards"])]]))]))))

(defmethod stats-area "Corp" [corp]
(let [ctrl (stat-controls-for-side :corp)]
(fn [corp]
(let [{:keys [user click credit bad-publicity active]} @corp
(let [{:keys [user click credit bad-publicity active trash-like-cards]} @corp
icons? (get-in @app-state [:options :player-stats-icons] true)]
[:div.stats-area
(if icons?
Expand All @@ -110,7 +117,14 @@
(ctrl :click [:div (tr [:game.click-count] click)])
(ctrl :credit [:div (tr [:game.credit-count] credit -1)])])
(let [{:keys [base additional]} bad-publicity]
(ctrl :bad-publicity [:div (tr [:game.bad-pub-count] base additional)]))]))))
(ctrl :bad-publicity [:div (tr [:game.bad-pub-count] base additional)]))
(when (= (:side @game-state) :corp)
(let [toggle-offer-trash #(send-command "set-property" {:key :trash-like-cards :delta (.. % -target -checked)})]
[:div [:label [:input {:type "checkbox"
:value true
:checked trash-like-cards
:on-click toggle-offer-trash}]
(tr [:game.trash-like-cards "Offer to trash like cards"])]]))]))))

(defn stats-view
[player]
Expand Down