From 7ad583a2bd2da5f5ad0de3a22ace042764212b43 Mon Sep 17 00:00:00 2001 From: NB Kelly Date: Sat, 31 Jan 2026 18:11:08 +1300 Subject: [PATCH 1/3] quick draft uses async paired prompts --- src/clj/game/core/quick_draft.clj | 215 ++++++++++++++++++++---------- 1 file changed, 142 insertions(+), 73 deletions(-) diff --git a/src/clj/game/core/quick_draft.clj b/src/clj/game/core/quick_draft.clj index 4d4f1e6345..068dd86945 100644 --- a/src/clj/game/core/quick_draft.clj +++ b/src/clj/game/core/quick_draft.clj @@ -1,14 +1,16 @@ (ns game.core.quick-draft (:require [game.core.card :refer [corp? ice? identity? agenda? runner? has-subtype? has-any-subtype? program?]] + [game.core.engine :refer [resolve-ability]] [game.core.initializing :refer [card-init make-card]] + [game.core.prompts :refer [show-wait-prompt show-prompt clear-wait-prompt]] [game.core.say :refer [system-msg]] [game.core.toasts :refer [toast]] [jinteki.cards :refer [all-cards]] [jinteki.utils :refer [other-side]] [jinteki.chimera :refer [corp-bans]] [game.core.eid :refer [effect-completed]] - [game.macros :refer [continue-ability req msg]] + [game.macros :refer [continue-ability req msg wait-for]] [game.utils :refer [server-card same-side?]])) (def runner-bans @@ -254,27 +256,27 @@ corp-info {:type :info :cards ["Hedge Fund" "Hedge Fund" "Hedge Fund" "Jackson Howard" "Jackson Howard"] :prompt "Your deck starts with 3 copies of Hedge Fund and 2 copies of Jackson Howard"}] - [corp-info - {:type :deck - :prompt "Choose an agenda (This game will be played to 6 points. You will receive 2 copies, and have 14 points in your deck)" - :qty 2 - :choices (take 5 (shuffle valid-3pointers))} - {:type :deck - :prompt "Choose an agenda (This game will be played to 6 points. You will receive 4 copies, and have 14 points in your deck)" - :qty 4 - :choices (take 5 (shuffle valid-2pointers))} - corp-ice - (generate-pick corp-format-cards 3 9 "card") - (generate-pick corp-format-cards 3 9 "card") - (generate-pick corp-format-cards 3 9 "card") - (generate-pick corp-format-cards 3 9 "card") - {:type :identity - :prompt "Choose your identity" - :choices (take 4 (shuffle valid-corp-ids))} - (generate-pick corp-format-cards 2 12 "card") - (generate-pick corp-format-cards 2 12 "card") - (generate-pick corp-format-cards 2 12 "card") - (generate-pick corp-format-cards 2 12 "card")])) + {:info corp-info + :stage-one [{:type :deck + :prompt "Choose an agenda (This game will be played to 6 points. You will receive 2 copies, and have 14 points in your deck)" + :qty 2 + :choices (take 5 (shuffle valid-3pointers))} + {:type :deck + :prompt "Choose an agenda (This game will be played to 6 points. You will receive 4 copies, and have 14 points in your deck)" + :qty 4 + :choices (take 5 (shuffle valid-2pointers))} + corp-ice + (generate-pick corp-format-cards 3 9 "card") + (generate-pick corp-format-cards 3 9 "card") + (generate-pick corp-format-cards 3 9 "card") + (generate-pick corp-format-cards 3 9 "card")] + :identity {:type :identity + :prompt "Choose your identity" + :choices (take 4 (shuffle valid-corp-ids))} + :stage-two [(generate-pick corp-format-cards 2 12 "card") + (generate-pick corp-format-cards 2 12 "card") + (generate-pick corp-format-cards 2 12 "card") + (generate-pick corp-format-cards 2 12 "card")]})) (def valid-runner-ids ["Hayley Kaplan: Universal Scholar" @@ -322,28 +324,50 @@ :cards ["Blueberry!™ Diesel" "Blueberry!™ Diesel" "Blueberry!™ Diesel" "Sure Gamble" "Sure Gamble" "Crypsis"] :prompt "Your deck starts with 3 copies of Blueberry!™ Diesel, 2 copies of Sure Gamble, and 1 copy of Crypsis"}] - [runner-info - (generate-pick fracters 2 6 "fracter") - (generate-pick decoders 2 6 "decoder") - (generate-pick killers 2 6 "killer") - ;; 12 - (generate-pick non-programs 3 9 "card") - (generate-pick runner-format-cards 3 10 "card") - (generate-pick non-programs 3 9 "card") - (generate-pick runner-format-cards 3 10 "card") - ;; 24 - {:type :identity - :prompt "Choose your identity" - :choices (take 4 (shuffle valid-runner-ids))} - (generate-pick non-programs 2 12 "card") - (generate-pick runner-format-cards 2 12 "card") - (generate-pick non-programs 2 12 "card") - (generate-pick runner-format-cards 2 12 "card")])) + {:info runner-info + :stage-one [(generate-pick fracters 2 6 "fracter") + (generate-pick decoders 2 6 "decoder") + (generate-pick killers 2 6 "killer") + (generate-pick non-programs 3 9 "card") + (generate-pick runner-format-cards 3 10 "card") + (generate-pick non-programs 3 9 "card") + (generate-pick runner-format-cards 3 10 "card")] + :identity {:type :identity + :prompt "Choose your identity" + :choices (take 4 (shuffle valid-runner-ids))} + :stage-two [(generate-pick non-programs 2 12 "card") + (generate-pick runner-format-cards 2 12 "card") + (generate-pick non-programs 2 12 "card") + (generate-pick runner-format-cards 2 12 "card")]})) + +(defn- combine [corp runner] + (vec (map (fn [c r] + {:type :deck + :corp c + :runner r}) + corp runner))) (defn- generate-quick-draft [] - {:corp (generate-corp-quick-draft) - :runner (generate-runner-quick-draft)}) + (let [corp (generate-corp-quick-draft) + runner (generate-runner-quick-draft) + corp-info (:info corp) + runner-info (:info runner) + corp-stage-one (:stage-one corp) + runner-stage-one (:stage-one runner) + corp-id (:identity corp) + runner-id (:identity runner) + corp-stage-two (:stage-two corp) + runner-stage-two (:stage-two runner)] + (concat + [{:type :info + :corp corp-info + :runner runner-info}] + (combine corp-stage-one runner-stage-one) + [{:type :identity + :corp corp-id + :runner runner-id}] + (combine corp-stage-two runner-stage-two)))) ;; so final format is: ;; Start with some quality @@ -383,40 +407,84 @@ ([state side qty card] (add-cards state side (repeat qty card)))) +(defn- maybe-complete + [state side eid f] + #(let [target (:value %) + os (other-side side) + other-side-complete? (-> @state :draft os)] + (wait-for + (f state side nil [target]) + (if-not other-side-complete? + ^:ignore-async-check + (do (swap! state assoc-in [:draft side] true) + (show-wait-prompt state side "your opponent to pick an option") + ;; do not complete an eid intentionally - it should remain open until both players complete + ) + (do (clear-wait-prompt state (other-side side)) + (effect-completed state side eid)))))) + +(defn- show-draft-prompt + [state side eid corp runner] + (swap! state assoc :draft {}) + (show-prompt state :corp nil (:prompt corp) (:choices corp) (maybe-complete state :corp eid (:effect corp)) {:prompt-type :draft}) + (show-prompt state :runner nil (:prompt runner) (:choices runner) (maybe-complete state :runner eid (:effect runner)) {:prompt-type :draft})) + +(defn- info-prompt + [{:keys [cards prompt]} remaining] + {:prompt (str prompt " - you have " remaining " picks to make") + :choices ["OK"] + :async true + :effect (req (add-cards state side cards) + (effect-completed state side eid))}) + +(defn- deck-prompt + [{:keys [choices prompt qty]} remaining] + {:prompt (str prompt " - you have " remaining " picks remaining") + :choices choices + :async true + :waiting-prompt true + :effect (req + (add-cards state side qty target) + (effect-completed state side eid))}) + +(defn- id-prompt + [state side corp runner remaining] + (let [runner-prompt {:prompt (str (:prompt runner) " - you have " remaining " picks remaining") + :choices (:choices runner) + :waiting-prompt true + :effect (req (system-msg state side (str "selects " target " as their identity")) + (set-id state side target))}] + {:prompt (str (:prompt corp) " - you have " remaining " picks remaining") + :choices (:choices corp) + :waiting-prompt true + :async true + :effect (req (system-msg state side (str "selects " target " as their identity")) + (continue-ability state :runner runner-prompt nil nil))})) + (defn- resolve-quick-draft [state side eid draft-queue] - (if (seq (side draft-queue)) - (let [[item & rem] (side draft-queue) - next-queue (assoc draft-queue side rem)] - (continue-ability - state side - (case (:type item) - :info {:prompt (str (:prompt item) " - you have " (dec (count (side draft-queue))) " picks to make") - :choices ["OK"] - :waiting-prompt true - :async true - :effect (req (add-cards state side (:cards item)) - (resolve-quick-draft state (other-side side) eid next-queue))} - :deck {:prompt (str (:prompt item) " - you have " (count (side draft-queue)) " picks remaining") - :choices (:choices item) - :async true - :waiting-prompt true - :effect (req - (add-cards state side (:qty item) target) - (resolve-quick-draft state (other-side side) eid next-queue))} - :identity {:prompt (str (:prompt item) " - you have " (count (side draft-queue)) " picks remaining") - :choices (:choices item) - :async true - :waiting-prompt true - :effect (req - (system-msg state side (str "selects " target " as their identity")) - (set-id state side target) - (resolve-quick-draft state (other-side side) eid next-queue))}) - nil nil)) + (if (seq draft-queue) + (let [{:keys [corp runner] :as item} (first draft-queue) + next-queue (rest draft-queue) + remaining (count next-queue)] + (case (:type item) + :info (wait-for + (show-draft-prompt + state side + (info-prompt corp remaining) + (info-prompt runner remaining)) + (resolve-quick-draft state side eid next-queue)) + :deck (wait-for + (show-draft-prompt + state side + (deck-prompt corp remaining) + (deck-prompt runner remaining)) + (resolve-quick-draft state side eid next-queue)) + :identity (wait-for + (resolve-ability state side (id-prompt state :corp corp runner remaining) nil nil) + (resolve-quick-draft state side eid next-queue)))) (do (swap! state update-in [:corp :deck] shuffle) (swap! state update-in [:runner :deck] shuffle) - (swap! state assoc-in [:runner :agenda-point-req] 6) - (swap! state assoc-in [:corp :agenda-point-req] 5) (effect-completed state side eid)))) (defn check-quick-draft @@ -424,5 +492,6 @@ (if (not= format "quick-draft") (effect-completed state nil eid) (let [draft-state (generate-quick-draft)] - (resolve-quick-draft - state :corp eid draft-state)))) + (swap! state assoc-in [:runner :agenda-point-req] 6) + (swap! state assoc-in [:corp :agenda-point-req] 5) + (resolve-quick-draft state :corp eid draft-state)))) From d26b1887277ec7226f3423f05f224a99c0edb38f Mon Sep 17 00:00:00 2001 From: NB Kelly Date: Sat, 31 Jan 2026 19:31:45 +1300 Subject: [PATCH 2/3] fixes tomorrow's headline --- src/clj/game/core/quick_draft.clj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/clj/game/core/quick_draft.clj b/src/clj/game/core/quick_draft.clj index 068dd86945..4e43c12afe 100644 --- a/src/clj/game/core/quick_draft.clj +++ b/src/clj/game/core/quick_draft.clj @@ -208,7 +208,7 @@ "Cyberdex Sandbox" "Broad Daylight" "Above the Law" - "Tomorrowʼs Headline" + "Tomorrow's Headline" "Project Beale"]) (def valid-corp-ids From 808d61cd5cc9197dba4af25cc782088798e27775 Mon Sep 17 00:00:00 2001 From: NB Kelly Date: Sun, 1 Feb 2026 07:46:18 +1300 Subject: [PATCH 3/3] dj/rebirth work --- src/clj/game/cards/events.clj | 3 +-- src/clj/game/cards/resources.clj | 3 +-- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/src/clj/game/cards/events.clj b/src/clj/game/cards/events.clj index 2a5afd8929..893f6e7f62 100644 --- a/src/clj/game/cards/events.clj +++ b/src/clj/game/cards/events.clj @@ -3026,8 +3026,7 @@ (= (:faction runner-identity) (:faction %)) (not (is-draft-id? %)) (not= (:title runner-identity) (:title %)) - (or (= :casual format) - (= :preconstructed format) + (or (#{:casual :quick-draft :preconstructed} format) (legal? format :legal %))) swappable-ids (filter is-swappable (server-cards))] (sort-by :title swappable-ids))) diff --git a/src/clj/game/cards/resources.clj b/src/clj/game/cards/resources.clj index f0cc9ea829..aec36c3326 100644 --- a/src/clj/game/cards/resources.clj +++ b/src/clj/game/cards/resources.clj @@ -1259,8 +1259,7 @@ (not= (-> runner :identity :faction) (:faction %)) (not (is-draft-id? %)) - (or (= :casual format) - (= :preconstructed format) + (or (#{:casual :quick-draft :preconstructed} format) (legal? format :legal %)))) (sort-by :title))) fenris-effect {:async true