diff --git a/.clj-kondo/config.edn b/.clj-kondo/config.edn index e5b95e7aeb..d1be9c79fd 100644 --- a/.clj-kondo/config.edn +++ b/.clj-kondo/config.edn @@ -52,58 +52,6 @@ game.core/effect game.core/wait-for]} :unused-namespace {:exclude ["game.core.*"]} :refer-all {:level :off} - :type-mismatch - {:namespaces - {monger.collection - {insert {:arities {3 {:args [:any :any :map]} - 4 {:args [:any :any :map :any]}}} - insert-and-return {:arities {3 {:args [:any :any :map]} - 4 {:args [:any :any :map :any]}}} - insert-batch {:arities {3 {:args [:any :any :seq]} - 4 {:args [:any :any :seq :any]}}} - find {:arities {2 {:args [:any :any]} - 3 {:args [:any :any :any]} - 4 {:args [:any :any :any :any]}}} - find-maps {:arities {2 {:args [:any :any]} - 3 {:args [:any :any :any]} - 4 {:args [:any :any :any :any]}}} - find-seq {:arities {2 {:args [:any :any]} - 3 {:args [:any :any :any]} - 4 {:args [:any :any :any :any]}}} - find-one {:arities {3 {:args [:any :any :any]} - 4 {:args [:any :any :any :any]}}} - find-one-as-map {:arities {3 {:args [:any :any :any]} - 4 {:args [:any :any :any :any]} - 5 {:args [:any :any :any :any :any]}}} - find-and-modify {:arities {5 {:args [:any :any :any :any :any]}}} - find-by-id {:arities {3 {:args [:any :any :any]} - 4 {:args [:any :any :any :any]}}} - find-map-by-id {:arities {3 {:args [:any :any :any]} - 4 {:args [:any :any :any :any]} - 5 {:args [:any :any :any :any :any]}}} - count {:arities {2 {:args [:any :any]} - 3 {:args [:any :any :any]}}} - any? {:arities {2 {:args [:any :any]} - 3 {:args [:any :any :any]}}} - empty? {:arities {2 {:args [:any :any]}}} - update {:arities {4 {:args [:any :any :any :any]} - 5 {:args [:any :any :any :any :any]}}} - upsert {:arities {4 {:args [:any :any :any :any]} - 5 {:args [:any :any :any :any :any]}}} - update-by-id {:arities {4 {:args [:any :any :any :any]} - 5 {:args [:any :any :any :any :any]}}} - update-by-ids {:arities {4 {:args [:any :any :any :any]} - 5 {:args [:any :any :any :any :any]}}} - save {:arities {3 {:args [:any :any :any]} - 4 {:args [:any :any :any :any]}}} - save-and-return {:arities {3 {:args [:any :any :any]} - 4 {:args [:any :any :any :any]}}} - remove {:arities {2 {:args [:any :any]} - 3 {:args [:any :any :any]}}} - remove-by-id {:arities {3 {:args [:any :any :any]}}} - purge-many {:arities {2 {:args [:any :any]}}} - create-index {:arities {3 {:args [:any :any :any]} - 4 {:args [:any :any :any :any]}}}}}} } :lint-as {game.test-framework/before-each clojure.core/let reagent.core/with-let clojure.core/let diff --git a/docker-compose.yml b/docker-compose.yml index 6819595dba..78161f0755 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -26,9 +26,12 @@ services: database: image: mongo - restart: always + container_name: mongodb + restart: unless-stopped ports: - - 27017:27017 + - 27017-27019:27017-27019 + volumes: + - ./data:/data/db cljs: entrypoint: npm run cljs:watch diff --git a/project.clj b/project.clj index 81348de602..730c564abe 100644 --- a/project.clj +++ b/project.clj @@ -18,8 +18,8 @@ :init-ns web.dev :init (go)} - :dependencies [[org.clojure/clojure "1.10.3"] - [org.clojure/clojurescript "1.10.891" + :dependencies [[org.clojure/clojure "1.11.2"] + [org.clojure/clojurescript "1.11.132" :exclusions [org.clojure/google-closure-library org.clojure/data.json org.clojure/tools.reader @@ -33,8 +33,6 @@ [ring/ring-devel "1.9.4" :exclusions [org.clojure/java.classpath]] [ring/ring-anti-forgery "1.3.0"] [ring/ring-json "0.5.1"] - [puppetlabs/ring-middleware "1.3.1" - :exclusions [prismatic/schema org.bouncycastle/bcpkix-jdk15on]] [ring-cors "0.1.13"] [compojure "1.6.2"] [hiccup "1.0.5"] diff --git a/src/clj/game/cards/agendas.clj b/src/clj/game/cards/agendas.clj index 6edc5aa2cf..a79ca98c27 100644 --- a/src/clj/game/cards/agendas.clj +++ b/src/clj/game/cards/agendas.clj @@ -1192,6 +1192,7 @@ :async true :optional {:prompt (msg "Remove 1 hosted agenda counter to rez up to 2 ice protecting " (zone->name (:server context)) ", ignoring all costs?") + :req (req (pos? (get-counters card :agenda))) :yes-ability {:cost [:agenda 1] :effect (req (let [current-server (first (:server (:run @state)))] diff --git a/src/clj/game/cards/assets.clj b/src/clj/game/cards/assets.clj index 1477990c8a..91fd115fd3 100644 --- a/src/clj/game/cards/assets.clj +++ b/src/clj/game/cards/assets.clj @@ -493,19 +493,23 @@ (defcard "Charlotte Caçador" (let [ability {:label "Gain 4 [Credits] and draw 1 card" - :optional {:once :per-turn - :prompt "Remove 1 hosted advancement counter to gain 4 [Credits] and draw 1 card?" - :req (req (pos? (get-counters card :advancement))) - :yes-ability {:msg "remove 1 hosted advancement counter from itself to gain 4 [Credits] and draw 1 card" - :async true - :effect (req - (add-prop state :corp card :advance-counter -1) - (wait-for - (gain-credits state side 4) - (draw state side eid 1)))}}} + :interactive (req true) + :optional + {:once :per-turn + :prompt "Remove 1 hosted advancement counter to gain 4 [Credits] and draw 1 card?" + :req (req (pos? (get-counters card :advancement))) + :yes-ability + {:msg "remove 1 hosted advancement counter from itself to gain 4 [Credits] and draw 1 card" + :async true + :effect (req + (add-prop state :corp card :advance-counter -1) + (wait-for + (gain-credits state side 4) + (draw state side eid 1)))}}} trash-ab {:cost [:advancement 1 :trash-can] :label "Gain 3 [Credits]" :msg (msg "gain 3 [Credits]") + :async true :effect (req (gain-credits state :corp eid 3))}] {:advanceable :always :flags {:corp-phase-12 (req true)} @@ -1240,32 +1244,37 @@ :choices {:card #(and (can-be-advanced? %) (installed? %))} :msg (msg "place 1 advancement counter on " (card-str state target)) - :effect (effect (add-prop target :advance-counter 1 {:placed true}))}] + :effect (effect (add-prop target :advance-counter 1 {:placed true}))} + ability {:req (req (:corp-phase-12 @state)) + :label "Move 1 hosted advancement counter to another card you can advance (start of turn)" + :once :per-turn + :waiting-prompt true + :prompt "Choose an installed card to move 1 hosted advancement counter from" + :choices {:card #(and (installed? %) + (get-counters % :advancement))} + :async true + :effect (effect + (continue-ability + (let [from-ice target] + {:prompt "Choose an installed card you can advance" + :choices {:card #(and (installed? %) + (can-be-advanced? %) + (not (same-card? from-ice %)))} + :msg (msg "move 1 hosted advancement counter from " + (card-str state from-ice) + " to " + (card-str state target)) + :async true + :effect (effect (add-prop :corp target :advance-counter 1) + (add-prop :corp from-ice :advance-counter -1) + (continue-ability political card nil)) + :cancel-effect (effect (continue-ability political card nil))}) + card nil)) + :cancel-effect (effect (continue-ability political card nil))}] {:derezzed-events [corp-rez-toast] :flags {:corp-phase-12 (req true)} - :abilities [{:label "Move 1 hosted advancement counter to another card you can advance (start of turn)" - :once :per-turn - :waiting-prompt true - :prompt "Choose an installed card to move 1 hosted advancement counter from" - :choices {:card #(and (installed? %) - (get-counters % :advancement))} - :effect (effect - (continue-ability - (let [from-ice target] - {:prompt "Choose an installed card you can advance" - :choices {:card #(and (installed? %) - (can-be-advanced? %) - (not (same-card? from-ice %)))} - :msg (msg "move 1 hosted advancement counter from " - (card-str state from-ice) - " to " - (card-str state target)) - :effect (effect (add-prop :corp target :advance-counter 1) - (add-prop :corp from-ice :advance-counter -1) - (continue-ability political card nil)) - :cancel-effect (effect (continue-ability political card nil))}) - card nil)) - :cancel-effect (effect (continue-ability political card nil))}]})) + :events [(assoc ability :event :corp-turn-begins)] + :abilities [ability]})) (defcard "Honeyfarm" {:flags {:rd-reveal (req true)} diff --git a/src/clj/game/cards/upgrades.clj b/src/clj/game/cards/upgrades.clj index 6baf82287c..7270719d6c 100644 --- a/src/clj/game/cards/upgrades.clj +++ b/src/clj/game/cards/upgrades.clj @@ -62,27 +62,22 @@ (defn mobile-sysop-event ([] (mobile-sysop-event :corp-turn-ends)) ([ev] (mobile-sysop-event ev nil)) - ([ev callback] {:event ev - :optional - {:prompt (msg "Move " (:title card) " to another server?") - :waiting-prompt true - :yes-ability - {:async true - :effect (effect (continue-ability - {:prompt "Choose a server" - :waiting-prompt true - :choices (server-list state) - :msg (msg "move itself to " target) - :async true - :effect (req (let [c (move state side card - (conj (server->zone state target) :content))] - (unregister-events state side card) - (register-default-events state side c) - (if callback - (continue-ability state side callback c nil) - (effect-completed state side eid)) - ))} - card nil))}}})) + ([ev callback] + {:event ev + :optional + {:prompt (msg "Move " (:title card) " to another server?") + :waiting-prompt true + :yes-ability + {:prompt "Choose a server" + :waiting-prompt true + :choices (req (server-list state)) + :msg (msg "move itself to " target) + :async true + :effect (req (let [c (move state side card + (conj (server->zone state target) :content))] + (unregister-events state side card) + (register-default-events state side c) + (continue-ability state side callback c nil)))}}})) ;; Card definitions diff --git a/src/clj/game/core/toasts.clj b/src/clj/game/core/toasts.clj index 5dac0dd91a..ca6a9bb437 100644 --- a/src/clj/game/core/toasts.clj +++ b/src/clj/game/core/toasts.clj @@ -1,6 +1,4 @@ -(ns game.core.toasts - (:require - [clj-uuid :as uuid])) +(ns game.core.toasts) (defn toast "Adds a message to toast with specified severity (default as a warning) to the toast message list. @@ -15,13 +13,14 @@ ([state side message msg-type] (toast state side message msg-type nil)) ([state side message msg-type options] ;; Allows passing just the toast msg-type as the options parameter - (when message + (when message ;; normal toast - add to list - (swap! state update-in [side :toast] #(conj % {:msg message :type msg-type :options options :id (uuid/v4)}))))) + (swap! state update-in [side :toast] #(conj % {:msg message :type msg-type :options options :id (random-uuid)}))))) (defn ack-toast ([state side {:keys [id]}] - (swap! state update-in [side :toast] (fn [toasts] (remove #(= (:id %) (uuid/as-uuid id)) toasts))))) + (when-let [id (when (string? id) (parse-uuid id))] + (swap! state update-in [side :toast] (fn [toasts] (remove #(= (:id %) id) toasts)))))) (defn show-error-toast [state side] diff --git a/src/clj/web/api.clj b/src/clj/web/api.clj index 855e35035d..718887a05d 100644 --- a/src/clj/web/api.clj +++ b/src/clj/web/api.clj @@ -1,7 +1,6 @@ (ns web.api (:require [cheshire.generate :refer [add-encoder encode-str]] - [puppetlabs.ring-middleware.core :refer [wrap-add-cache-headers]] [reitit.core :as r] [reitit.ring :as ring] [ring.middleware.anti-forgery :refer [wrap-anti-forgery]] @@ -38,6 +37,20 @@ ((ring/ring-handler (base-routes)) {:request-method :get :uri "/"}) ) +;; Taken from puppetlabs/ring-middleware +(defn wrap-add-cache-headers + "Adds cache control invalidation headers to GET and PUT requests if they are handled by the handler" + [handler] + (fn [request] + (let [request-method (:request-method request) + response (handler request)] + (when-not (nil? response) + (if (or + (= request-method :get) + (= request-method :put)) + (assoc-in response [:headers "cache-control"] "no-store") + response))))) + (defn api-routes [] (ring/router [["/chsk" {:get ws/handshake-handler diff --git a/src/clj/web/data.clj b/src/clj/web/data.clj index 0ed8280570..c950bb9e99 100644 --- a/src/clj/web/data.clj +++ b/src/clj/web/data.clj @@ -6,28 +6,22 @@ [clojure.edn :as edn])) (defn news-handler [{db :system/db}] - (let [data (mq/with-collection db "news" + (let [data (mq/with-collection db (.getCollection db "news") (mq/find {}) (mq/fields [:_id :item :date]) (mq/sort (array-map :date -1))) - data (map #(update % :date mongo-time-to-utc-string) data)] + data (mapv #(update % :date mongo-time-to-utc-string) data)] (response 200 data))) -(defn- cards-version-impl [db] +(defn- cards-version [db] (:cards-version (mc/find-one-as-map db "config" nil))) -(def cards-version (memoize cards-version-impl)) - (defn cards-version-handler [{db :system/db}] (response 200 {:version (int (cards-version db))})) -(defn- enriched-cards-impl [db] +(defn- enriched-cards [db] (let [cards (mc/find-maps db "cards")] - (->> cards - (map #(assoc % :implementation (card-implemented %))) - (map #(dissoc % :_id))))) - -(def enriched-cards (memoize enriched-cards-impl)) + (mapv #(-> % (assoc :implementation (card-implemented %)) (dissoc :_id)) cards))) (defn cards-handler [{db :system/db}] (response 200 (enriched-cards db))) @@ -38,29 +32,29 @@ (defn lang-handler [{db :system/db {lang :lang} :path-params}] (if (validate-lang lang) - (response 200 (map #(dissoc % :_id) (mc/find-maps db (str "cards-" lang)))) - (response 200 {}))) + (response 200 (mapv #(dissoc % :_id) (mc/find-maps db (str "cards-" lang)))) + (response 200 {}))) (defn alt-arts-handler [{db :system/db}] - (response 200 (map #(dissoc % :_id) (mc/find-maps db "altarts")))) + (response 200 (mapv #(dissoc % :_id) (mc/find-maps db "altarts")))) (defn sets-handler [{db :system/db}] - (response 200 (map #(dissoc % :_id) (mc/find-maps db "sets")))) + (response 200 (mapv #(dissoc % :_id) (mc/find-maps db "sets")))) (defn mwl-handler [{db :system/db}] - (response 200 (map #(dissoc % :_id) (mc/find-maps db "mwls")))) + (response 200 (mapv #(dissoc % :_id) (mc/find-maps db "mwls")))) (defn cycles-handler [{db :system/db}] - (response 200 (map #(dissoc % :_id) (mc/find-maps db "cycles")))) + (response 200 (mapv #(dissoc % :_id) (mc/find-maps db "cycles")))) (defn donors-handler [{db :system/db}] (response 200 (->> (mc/find-maps db "donators") - (map #(let [amount (:amount %)] - (assoc % :amount (if (string? amount) - (edn/read-string amount) - amount)))) + (mapv #(let [amount (:amount %)] + (assoc % :amount (if (string? amount) + (edn/read-string amount) + amount)))) (sort-by :amount >) - (map #(let [username (:username %)] - (if (empty? username) - (:name %) - username)))))) + (mapv #(let [username (:username %)] + (if (empty? username) + (:name %) + username)))))) diff --git a/src/clj/web/system.clj b/src/clj/web/system.clj index ec1aed9aa0..3ebcbc088d 100644 --- a/src/clj/web/system.clj +++ b/src/clj/web/system.clj @@ -137,8 +137,8 @@ (defmethod ig/init-key :jinteki/cards [_ {{:keys [db]} :mongo}] (let [cards (mc/find-maps db "cards" nil) - stripped-cards (map #(update % :_id str) cards) - all-cards (into {} (map (juxt :title identity) stripped-cards)) + stripped-cards (mapv #(update % :_id str) cards) + all-cards (into {} (map (juxt :title identity)) stripped-cards) sets (mc/find-maps db "sets" nil) cycles (mc/find-maps db "cycles" nil) mwl (mc/find-maps db "mwls" nil) diff --git a/test/clj/game/cards/assets_test.clj b/test/clj/game/cards/assets_test.clj index d3405489be..5c6a6134cd 100644 --- a/test/clj/game/cards/assets_test.clj +++ b/test/clj/game/cards/assets_test.clj @@ -780,6 +780,24 @@ "Corp gaind 3 credits") (is (nil? (refresh cc)) "Charlotte Caçador got trashed")))) +(deftest charlotte-cacador-la-costa + (do-game + (new-game {:corp {:hand ["Charlotte Caçador" "La Costa Grid"] + :deck [(qty "Hedge Fund" 5)] + :credits 100}}) + (play-from-hand state :corp "Charlotte Caçador" "New remote") + (play-from-hand state :corp "La Costa Grid" "Server 1") + (let [cc (get-content state :remote1 0) + lcg (get-content state :remote1 1)] + (advance state cc 2) + (rez state :corp cc) + (rez state :corp lcg) + (take-credits state :corp) + (take-credits state :runner) + (is (:corp-phase-12 @state) "Corp has opportunity to use Charlotte Caçador") + (end-phase-12 state :corp) + (is (some #{"Charlotte Caçador" "La Costa Grid"} (mapv :title (prompt-buttons :corp))))))) + (deftest chekist-scion ;; Chekist Scion (do-game @@ -2559,6 +2577,22 @@ (is (= 1 (get-counters (refresh haa) :advancement)) "Can't use twice in a turn") (is (= 2 (:click (get-corp))) "Didn't spend a click")))) +(deftest hearts-and-minds + (do-game + (new-game {:corp {:hand ["Hearts and Minds" "NGO Front"]}}) + (play-from-hand state :corp "Hearts and Minds" "New remote") + (play-from-hand state :corp "NGO Front" "New remote") + (let [ham (get-content state :remote1 0) + ngo (get-content state :remote2 0)] + (rez state :corp ham) + (take-credits state :corp) + (take-credits state :runner) + (card-ability state :corp ham 0) + (click-prompt state :corp "Done") + (is (changed? [(get-counters (refresh ngo) :advancement) 1] + (click-card state :corp ngo)) + "NGO Front got 1 advancement counter")))) + (deftest hearts-and-minds-behind-ice (do-game (new-game {:corp {:hand ["Hearts and Minds" "Vanilla" "NGO Front" "Project Atlas"]}}) @@ -2583,22 +2617,6 @@ "Advancement counter moved from NGO Front to Project Atlas") (is (no-prompt? state :corp) "No additional prompt because Hearts and Minds is behind ice")))) -(deftest hearts-and-minds - (do-game - (new-game {:corp {:hand ["Hearts and Minds" "NGO Front"]}}) - (play-from-hand state :corp "Hearts and Minds" "New remote") - (play-from-hand state :corp "NGO Front" "New remote") - (let [ham (get-content state :remote1 0) - ngo (get-content state :remote2 0)] - (rez state :corp ham) - (take-credits state :corp) - (take-credits state :runner) - (card-ability state :corp ham 0) - (click-prompt state :corp "Done") - (is (changed? [(get-counters (refresh ngo) :advancement) 1] - (click-card state :corp ngo)) - "NGO Front got 1 advancement counter")))) - (deftest honeyfarm ;; Honeyfarm - lose one credit on access (do-game diff --git a/test/clj/game/cards/upgrades_test.clj b/test/clj/game/cards/upgrades_test.clj index fb48cef3e6..0825c26f9b 100644 --- a/test/clj/game/cards/upgrades_test.clj +++ b/test/clj/game/cards/upgrades_test.clj @@ -1892,7 +1892,8 @@ (deftest isaac-liberdade (do-game - (new-game {:corp {:hand ["Isaac Liberdade" "Ice Wall" "Tithe"]}}) + (new-game {:corp {:hand ["Isaac Liberdade" "Ice Wall" "Tithe"] + :credits 10}}) (core/gain state :corp :click 1) (play-from-hand state :corp "Isaac Liberdade" "New remote") (play-from-hand state :corp "Ice Wall" "Server 1")