Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
50 commits
Select commit Hold shift + click to select a range
ad8d2eb
melies-u image handling
NBKelly Feb 14, 2026
a44a56c
Meleis U: Only the Brightest
NBKelly Feb 14, 2026
621e6d8
Virtual Intelligence, PI
NBKelly Feb 14, 2026
1a8ebeb
remove unused import
NBKelly Feb 15, 2026
295ba13
functional rules change for bad publicity
NBKelly Feb 15, 2026
9ba9173
unit tests updated for the bad publicity rule change
NBKelly Feb 15, 2026
ce3d1e2
Editorial Division, Nihilo Agent, Vulture Fund
NBKelly Feb 15, 2026
50d979c
hiram 0mission svensson
NBKelly Feb 16, 2026
dcd0065
qol for meiles u
NBKelly Feb 16, 2026
23dac31
side doing the damage trashes the cards
NBKelly Feb 17, 2026
81e034a
let them dream, melies city luxury line
NBKelly Feb 18, 2026
4b688d1
startup validation is three 3+pointers nows
NBKelly Feb 18, 2026
6762650
fixed tests
NBKelly Feb 18, 2026
edc7665
less hacky bad pub routing
NBKelly Feb 18, 2026
e086444
stick and poke
NBKelly Feb 18, 2026
4329a80
magistrate revontulet
NBKelly Feb 19, 2026
0e35973
flagship, access updates
NBKelly Feb 19, 2026
13b105b
Merge branch 'master' into vantage-point-spoilers-2
NBKelly Feb 21, 2026
88276c9
Merge branch 'master' of github.com:mtgred/netrunner into vantage-poi…
NBKelly Feb 21, 2026
7edb010
compiles
NBKelly Feb 21, 2026
09f0974
Async
NBKelly Feb 21, 2026
c85c9ce
tailgate, kompromat
NBKelly Feb 21, 2026
efdd9e2
witch hunt test
NBKelly Feb 21, 2026
116f06d
paywall
NBKelly Feb 21, 2026
ee23fa5
bad pub redo now works with unit tests
NBKelly Feb 21, 2026
54bf283
flywheel, event horizon
NBKelly Feb 21, 2026
b90e2a5
stowaway
NBKelly Feb 21, 2026
f063a4d
myoshu and tests
NBKelly Feb 21, 2026
dd03d1a
beta build
NBKelly Feb 22, 2026
db91d80
borrowed goods
NBKelly Feb 22, 2026
4fba084
rules says there will be an upadte to make flagship not silly with cu…
NBKelly Feb 23, 2026
13be4c3
dont prune cards from the set of already accessed cards
NBKelly Feb 23, 2026
df76104
touchstone, rotary
NBKelly Feb 23, 2026
24c5158
knowledge seeker
NBKelly Feb 23, 2026
9bda6ed
Ansel 2.0, baker
NBKelly Feb 23, 2026
f05934b
tocsin, viksek, vertigo
NBKelly Feb 24, 2026
78305e4
unit tests for the ice
NBKelly Feb 24, 2026
fa92c9f
esca
NBKelly Feb 24, 2026
046c862
unleash and tests
NBKelly Feb 24, 2026
e3306de
ezaM and tests
NBKelly Feb 24, 2026
d88fb11
The Red Room and tests
NBKelly Feb 24, 2026
2e08750
show other player discard option for prompts
NBKelly Feb 24, 2026
98bc6bd
Methuselah test
NBKelly Feb 26, 2026
157b1af
hype machine and test
NBKelly Feb 26, 2026
6fe8951
read-write share
NBKelly Feb 26, 2026
20fd10d
grubber and unit etsts
NBKelly Feb 26, 2026
75864f5
support for cards with hosting discounts to work for cost computation
NBKelly Feb 26, 2026
2519f3a
chain reaction and test
NBKelly Feb 26, 2026
97978ad
hackerspace and test, test for class act triggering correctly on host…
NBKelly Feb 26, 2026
9894225
perfect recall and test
NBKelly Feb 26, 2026
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
60 changes: 58 additions & 2 deletions src/clj/game/cards/agendas.clj
Original file line number Diff line number Diff line change
Expand Up @@ -43,15 +43,15 @@
[game.core.prompts :refer [cancellable clear-wait-prompt show-wait-prompt]]
[game.core.props :refer [add-counter add-prop]]
[game.core.purging :refer [purge]]
[game.core.revealing :refer [reveal]]
[game.core.revealing :refer [reveal reveal-loud]]
[game.core.rezzing :refer [derez rez rez-multiple-cards]]
[game.core.runs :refer [clear-encounter end-run get-current-encounter force-ice-encounter redirect-run start-next-phase]]
[game.core.say :refer [play-sfx system-msg]]
[game.core.servers :refer [is-remote? target-server zone->name]]
[game.core.set-aside :refer [set-aside-for-me]]
[game.core.shuffling :refer [shuffle! shuffle-into-deck shuffle-my-deck!
shuffle-into-rd-effect]]
[game.core.tags :refer [gain-tags]]
[game.core.tags :refer [gain-tags lose-tags]]
[game.core.to-string :refer [card-str]]
[game.core.toasts :refer [toast]]
[game.core.update :refer [update!]]
Expand Down Expand Up @@ -1281,6 +1281,38 @@
:duration :end-of-run})
(effect-completed state side eid)))}}]})

(defcard "Let Them Dream"
(letfn [(move-to [c from]
(choose-one-helper
(let [and-then (fn [s] (str (if (= from :rd) ", shuffle R&D, and then " " and ") s))]
{:prompt (str "Move " (:title c) " where?")}
[{:option "HQ"
:ability {:async true
:effect (req (when (= from :rd) (shuffle! state side :deck))
(move state side c :hand)
(reveal-loud state side eid card {:and-then (and-then "add it to HQ")} c))}}
{:option "Bottom of R&D"
:ability {:async true
:effect (req (when (= from :rd) (shuffle! state side :deck))
(move state side c :deck)
(reveal-loud state side eid card {:and-then (and-then "add it to the bottom of R&D")} c))}}])))
(find-ab [zone]
{:prompt "Choose an agenda"
:show-discard (= zone :archives)
:choices (if (= zone :rd)
(req (cancellable (filter agenda? (:deck corp)) :sorted))
{:card #(and (agenda? %) (if (= zone :hq) (in-hand? %) (in-discard? %)))})
:effect (req (continue-ability state side (move-to target zone) card nil))
:async true
:cancel-effect shuffle-my-deck!})]
{:on-score (choose-one-helper
{:optional true
:prompt "Search for an Agenda from where?"}
[{:option "HQ" :ability (find-ab :hq)}
{:option "R&D" :ability (find-ab :rd)}
{:option "Archives" :ability (find-ab :archives)}])
:agendapoints-runner (req 1)}))

(defcard "License Acquisition"
{:on-score {:interactive (req true)
:prompt "Choose an asset or upgrade to install from Archives or HQ"
Expand Down Expand Up @@ -1409,6 +1441,11 @@
:req (req (= (:title target) "Medical Breakthrough"))
:value -1}]})

(defcard "Méliès City Luxury Line"
{:steal-cost-bonus (req [(->c :click 1)])
:on-score {:msg "gain [Click]"
:effect (req (gain-clicks state :corp 1))}})

(defcard "Megaprix Qualifier"
{:on-score {:silent (req true)
:req (req (< 1 (count (filter #(= (:title %) "Megaprix Qualifier")
Expand Down Expand Up @@ -2545,3 +2582,22 @@
(not (has-subtype? target "Virtual"))
(not (:facedown (second targets)))))
:value 1}]})

(defcard "Witch Hunt"
(let [bp {:msg "take 1 bad publicity"
:async true
:effect (effect (gain-bad-publicity :corp eid 1))}]
{:stolen bp
:on-score bp
:events [{:unregister-once-resolved true
:event :corp-action-phase-ends
:duration :end-of-turn
:req (effect (first-event? :agenda-scored #(same-card? card (:card (first %)))))
:msg (msg (if tagged
"Remove all tags, and then give the Runner 3 tags"
"give the Runner 3 tags"))
:async true
:effect (req (if tagged
(wait-for (lose-tags state side :all {:suppress-checkpoint true})
(gain-tags state side eid 3))
(gain-tags state side eid 3)))}]}))
45 changes: 44 additions & 1 deletion src/clj/game/cards/assets.clj
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@
[game.core.set-aside :refer [swap-set-aside-cards]]
[game.core.shuffling :refer [shuffle! shuffle-into-deck shuffle-my-deck!
shuffle-into-rd-effect]]
[game.core.tags :refer [gain-tags]]
[game.core.tags :refer [gain-tags lose-tags]]
[game.core.threat :refer [threat-level]]
[game.core.to-string :refer [card-str]]
[game.core.toasts :refer [toast]]
Expand Down Expand Up @@ -1071,6 +1071,20 @@
:req (req (installed? target))
:value 1}]})

(defcard "Esca"
{:flags {:rd-reveal (req true)}
:poison true
:on-access {:msg "force the Runner to lose 1 [Credits]"
:async true
:effect (req (wait-for (lose-credits state :runner 1)
(continue-ability
state side
{:req (req tagged)
:msg "do 1 net damage"
:async true
:effect (req (damage state side eid :net 1))}
card nil)))}})

(defcard "Estelle Moon"
{:events [{:event :corp-install
:req (req (and (or (asset? (:card context))
Expand Down Expand Up @@ -1843,6 +1857,16 @@
:effect (req (access-bonus state :runner target -1))}
card targets))}]})

(defcard "Magistrate Revontulet"
{:static-abilities [{:type :steal-additional-cost
:req (req (agenda? target))
:value (req [(->c :credit 3)])}]
:events [{:event :agenda-scored
:async true
:interactive (req true)
:msg "force the Runner to lose 3 [Credits]"
:effect (req (lose-credits state :runner eid 3))}]})

(defcard "Malia Z0L0K4"
(let [unmark
(req (when-let [malia-target (get-in card [:special :malia-target])]
Expand Down Expand Up @@ -2240,6 +2264,25 @@
(do (as-agenda state :runner card -1)
(effect-completed state side eid))))}})

(defcard "Nihilo Agent"
{:data {:counter {:power 3}}
:events [(trash-on-empty :power)
{:event :corp-turn-ends
:msg "take 1 bad publicity and give the Runner 1 tag"
:async true
:effect (req (wait-for
(gain-bad-publicity state :corp 1 {:suppress-checkpoint true})
(wait-for
(add-counter state side card :power -1 {:suppress-checkpoint true})
(gain-tags state side eid 1))))}
{:event :corp-turn-begins
:change-in-game-state {:silent true
:req (req (or tagged (pos? (count-bad-pub state))))}
:msg "remove 1 bad publicity and 1 tag"
:async true
:effect (req (wait-for (lose-bad-publicity state :corp 1 {:suppress-checkpoint true})
(lose-tags state side eid 1)))}]})

(defcard "Open Forum"
{:events [{:event :corp-mandatory-draw
:interactive (req true)
Expand Down
109 changes: 109 additions & 0 deletions src/clj/game/cards/events.clj
Original file line number Diff line number Diff line change
Expand Up @@ -264,6 +264,32 @@
(move state :corp c :deck))
(shuffle! state :corp :deck))}})]})

(defcard "Beta Build"
{:makes-run true
:on-play {:async true
:effect (req (wait-for
(resolve-ability
state side
{:prompt "Install a non-virus program"
:choices (req (cancellable (filter #(and (program? %)
(runner-can-install? state side eid % {:no-toast true}))
(:deck runner))))
:async true
:effect (req (wait-for
(runner-install state side target {:ignore-all-cost :true :msg-keys {:display-origin true :source-card card}})
(complete-with-result state side eid async-result)))}
card nil)
(let [installed-card async-result]
(resolve-ability state side eid
(run-any-server-ability {:events [{:event :run-ends
:unregister-once-resolved true
:duration :end-of-run
:change-in-game-state {:silent true
:req (req (get-card state installed-card))}
:msg (msg "add " (:title installed-card) " to the top of the stack")
:effect (req (move state side installed-card :deck {:front true}))}]})
card nil))))}})

(defcard "Black Hat"
{:on-play
{:trace
Expand Down Expand Up @@ -561,6 +587,39 @@
(cbi-choice from '() (count from) from)))
card nil))}})]}))

(defcard "Chain Reaction"
(let [corp-choice {:player :corp
:prompt "Choose a Runner card to trash"
:async true
:req (req (seq (all-installed state :runner)))
:choices {:card (every-pred runner? installed?)}
:waiting-prompt true
:display-side :corp
:msg (msg "trash " (:title target))
:effect (req (trash state :corp eid target))}
cards-to-trash (fn [state] (min 2 (count (all-installed state :corp))))
runner-choice {:prompt (msg "choose " (quantify (cards-to-trash state) "card") " to trash")
:async true
:choices {:card (every-pred corp? installed?)
:max (req (cards-to-trash state))
:all true}
:waiting-prompt true
:msg (msg "trash " (enumerate-str (map #(card-str state %) targets)))
:effect (req (wait-for (trash-cards state side targets)
(continue-ability
state :corp
corp-choice
card nil)))}]
{:on-play {:async true
:change-in-game-state {:req (req (or (seq (all-installed state :corp))
(seq (all-installed state :runner))))}
:req (req (and (some #{:hq} (:successful-run runner-reg))
(some #{:rd} (:successful-run runner-reg))
(some #{:archives} (:successful-run runner-reg))))
:effect (req (if (seq (all-installed state :corp))
(continue-ability state side runner-choice card nil)
(continue-ability state :corp corp-choice card nil)))}}))

(defcard "Charm Offensive"
(letfn [(trash-x-opt [t]
{:option (str "Trash a rezzed copy of " t)
Expand Down Expand Up @@ -2180,6 +2239,45 @@
(defcard "Knifed"
(cutlery "Barrier"))

(defcard "Kompromat"
(letfn [(iced-servers [state side eid card]
(filter #(-> (get-in @state (cons :corp (server->zone state %))) :ices count pos?)
(zones->sorted-names (get-runnable-zones state side eid card nil))))]
{:makes-run true
:on-play {:async true
:rfg-instead-of-trashing true
:change-in-game-state {:req (req (seq (iced-servers state side eid card)))}
:prompt "Choose an iced server"
:choices (req (iced-servers state side eid card))
:effect (req (make-run state side eid target card))}
:events [{:event :run-ends
:req (req (and this-card-run (:successful context)))
:async true
:interactive (req true)
:effect (req (let [valid-ice (filter #(and (ice? %)
(rezzed? %)
(= (first (:server context)) (second (get-zone %))))
(all-installed state :corp))]
(continue-ability
state side
(if (seq valid-ice)
{:prompt "Derez an ice? (if you click done, you take a bad publicity)"
:player :corp
:waiting-prompt true
:choices {:req (req (some #(same-card? % target) valid-ice))}
:cancel {:display-side :runner
:msg "give the Corp 1 bad publicity"
:async true
:effect (req (gain-bad-publicity state :runner eid 1))}
:msg (msg "derez " (card-str state target))
:display-side :corp
:async true
:effect (req (derez state side eid target {:no-msg true}))}
{:msg "give the Corp 1 bad publicity"
:async true
:effect (req (gain-bad-publicity state :runner eid 1))})
card nil)))}]}))

(defcard "Kraken"
{:on-play
{:req (req (:stole-agenda runner-reg))
Expand Down Expand Up @@ -3761,6 +3859,17 @@
(assoc ability :event :corp-turn-ends)
(assoc ability :event :runner-turn-ends)]}))

(defcard "Tailgate"
{:makes-run true
:on-play (run-server-ability
:hq
{:play-cost-bonus (req (- (count (get-in @state [:corp :servers :hq :ices]))))})
:events [{:event :successful-run
:silent (req true)
:req (req (and (= :hq (target-server context)) this-card-run))
:effect (effect (register-events
card [(breach-access-bonus :hq 2 {:duration :end-of-run})]))}]})

(defcard "Test Run"
{:on-play
{:prompt (req (if (not (zone-locked? state :runner :discard))
Expand Down
49 changes: 48 additions & 1 deletion src/clj/game/cards/hardware.clj
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@
get-current-encounter jack-out make-run
successful-run-replace-breach total-cards-accessed]]
[game.core.say :refer [play-sfx system-msg]]
[game.core.servers :refer [target-server is-central?]]
[game.core.servers :refer [target-server is-central? zone->name]]
[game.core.shuffling :refer [shuffle!]]
[game.core.tags :refer [gain-tags lose-tags]]
[game.core.threat :refer [threat-level]]
Expand Down Expand Up @@ -444,6 +444,14 @@
:deck)
(shuffle! :deck))}}}])))}]}))

(defcard "Borrowed Goods"
{:on-install {:change-in-game-state {:req (req (not tagged)) :silent true}
:msg "take 1 tag"
:interactive (req true)
:async true
:effect (req (gain-tags state side eid 1))}
:static-abilities [(mu+ 1)]})

(defcard "Box-E"
{:static-abilities [(mu+ 2)
(runner-hand-size+ 2)]})
Expand Down Expand Up @@ -1604,6 +1612,20 @@
(defcard "MemStrips"
{:static-abilities [(virus-mu+ 3)]})

(defcard "Methuselah"
{:interactions {:pay-credits {:req (req run)
:type :credit}}
:events [{:event :run
:change-in-game-state {:req (req (seq (:hand runner))) :silent true}
:prompt "Trash a hardware from the Grip?"
:choices {:card (every-pred hardware? in-hand?)}
:async true
:waiting-prompt true
:msg (msg "trash " (:title target) " and place 2 [Credits] on itself")
:effect (req (wait-for (trash state side target {:unpreventable true})
(add-counter state side eid card :credit 2)))}]
:static-abilities [(mu+ 1)]})

(defcard "Mind's Eye"
{:implementation "Power counters added automatically"
:static-abilities [(mu+ 1)]
Expand Down Expand Up @@ -2207,6 +2229,23 @@
:effect (effect (continue-ability ability card nil))}]
:abilities [ability]}))

(defcard "Rotary"
{:static-abilities [(mu+ 1)]
:events [{:event :breach-server
:automatic :pre-breach
:optional {:req (req (or (= target :rd) (= target :hq)))
:prompt "Tag 1 tag to see an additional card?"
:yes-ability {:cost [(->c :gain-tag 1)]
:msg (msg "access 1 additional card from " (zone->name target))
:effect (effect (access-bonus target 1))}}}]
:corp-abilities [{:action true
:label "Trash Rotary"
:async true
:cost [(->c :click 1) (->c :credit 2)]
:req (req (and tagged (= :corp side)))
:effect (effect (system-msg :corp "spends [Click] and 2 [Credits] to trash Rotary")
(trash :corp eid card {:cause-card card}))}]})

(defcard "Rubicon Switch"
{:abilities [{:action true
:cost [(->c :click 1)(->c :x-credits)]
Expand Down Expand Up @@ -2648,6 +2687,14 @@
(effect-completed state nil eid)
(access-card state side eid (nth (:deck corp) (dec (str->int target))) "an unseen card")))}})]})

(defcard "Touchstone"
{:events [{:event :play-event
:req (req (first-event? state side :play-event))
:async true
:effect (req (add-counter state side eid card :credit 1))}]
:interactions {:pay-credits {:req (req run)
:type :credit}}})

(defcard "Turntable"
{:static-abilities [(mu+ 1)]
:events [{:event :agenda-stolen
Expand Down
Loading
Loading