diff options
| -rw-r--r-- | src/votann/battle_simulator_widget.clj | 140 | ||||
| -rw-r--r-- | src/votann/codex.clj | 5 | ||||
| -rw-r--r-- | src/votann/event_handler.clj | 155 | ||||
| -rw-r--r-- | src/votann/tab_widget.clj | 8 | ||||
| -rw-r--r-- | src/votann/util.clj | 24 | ||||
| -rw-r--r-- | src/votann_battle_simulator/battle_round.clj | 87 | ||||
| -rw-r--r-- | src/votann_battle_simulator/util.clj | 46 | ||||
| -rw-r--r-- | src/votann_battle_simulator/weapon_abilities.clj | 185 |
8 files changed, 624 insertions, 26 deletions
diff --git a/src/votann/battle_simulator_widget.clj b/src/votann/battle_simulator_widget.clj new file mode 100644 index 0000000..cfc383d --- /dev/null +++ b/src/votann/battle_simulator_widget.clj @@ -0,0 +1,140 @@ +(ns votann.battle-simulator-widget + (:require [votann.codex :refer [kin-models kin-bodyguards]] + [votann.util :as util] + [cljfx.api :as fx])) + +(def models-widget-select + {:fx/type :choice-box + :on-action {:event/type :event/choice-select-model} + :value (:name (first (apply conj kin-models kin-bodyguards))) + :items (mapv #(:name %) (apply conj kin-models kin-bodyguards))}) + +(def models-widget-target + {:fx/type :choice-box + :on-action {:event/type :event/choice-select-target} + :value (:name (first (apply conj kin-models kin-bodyguards))) + :items (mapv #(:name %) (apply conj kin-models kin-bodyguards))}) + +(defn chart-data [data] + (vec (for [d data] + {:fx/type :xy-chart-data :x-value (:damage d) :y-value (:weapon d)}))) + +(defn damage-data [data type] + (vec (for [[weapon damage] data] + (cond (= "Mean" type) + {:fx/type :xy-chart-data :x-value (util/mean damage) :y-value weapon} + + (= "Median" type) + {:fx/type :xy-chart-data :x-value (util/median damage) :y-value weapon} + + (= "Total" type) + {:fx/type :xy-chart-data :x-value (util/total damage) :y-value weapon})))) + +(defn battle-simulator-widget [{:keys [data total-damage data-type count weapon-type rolls target-count tokens disabled]}] + {:fx/type :v-box + :children [{:fx/type :h-box + :alignment :center + :padding 12 + :spacing 12 + :children [ + {:fx/type :v-box + :children [ + {:fx/type :label + :text "Weapon Type"} + {:fx/type :choice-box + :on-action {:event/type :event/choice-select-weapon} + :value weapon-type + :items ["Ranged" + "Melee" + ]}]} + {:fx/type :v-box + :children [ + {:fx/type :label + :text "Model"} + models-widget-select]} + {:fx/type :v-box + :children [ + {:fx/type :label + :text "Model Count"} + {:fx/type :text-field + :on-key-typed {:event/type :event/choice-select-rolls} + :max-width 80 + :text rolls} + ]} + {:fx/type :v-box + :children [ + {:fx/type :label + :text "Target"} + models-widget-target]} + {:fx/type :v-box + :children [ + {:fx/type :label + :text "Target Count"} + {:fx/type :text-field + :on-key-typed {:event/type :event/choice-select-target-size} + :max-width 82 + :text target-count} + ]} + {:fx/type :v-box + :children [ + {:fx/type :label + :text "Judgment Tokens"} + {:fx/type :choice-box + :min-width 108 + :on-action {:event/type :event/choice-select-tokens} + :value tokens + :items ["0 " + "1 " + "2 "]}]} + {:fx/type :v-box + :children [ + {:fx/type :label + :text ""} + {:fx/type :button + :disable disabled + :on-action {:event/type :event/choice-select-start} + :text "Roll"} + ]} + {:fx/type :v-box + :children [ + {:fx/type :label + :text ""} + {:fx/type :button + :on-action {:event/type :event/choice-select-reset} + :text "Reset"} + ]} + ]} + {:fx/type util/ext-recreate-on-key-changed + :key data + :desc + {:fx/type :bar-chart + :title (:dialog (first data)) + :legend-visible false + :x-axis {:fx/type :number-axis :label "Damage"} + :y-axis {:fx/type :category-axis} + :data [{:fx/type :xy-chart-series + :data (chart-data data)}]}} + + {:fx/type :h-box + :alignment :center + :spacing 12 + :children [{:fx/type :label + :style "-fx-font-size: 18px;" + :text (str "Weapon damage after " count " rolls")} + {:fx/type :choice-box + :on-action {:event/type :event/choice-select-stats} + :value data-type + :items ["Total" + "Mean" + "Median"]}] + } + + {:fx/type util/ext-recreate-on-key-changed + :key total-damage + :desc + {:fx/type :bar-chart + :legend-visible false + :x-axis {:fx/type :number-axis :label "Damage"} + :y-axis {:fx/type :category-axis} + :data [{:fx/type :xy-chart-series + :data (damage-data total-damage data-type)}]}}]}) diff --git a/src/votann/codex.clj b/src/votann/codex.clj index 6e2cd17..96768f5 100644 --- a/src/votann/codex.clj +++ b/src/votann/codex.clj @@ -17,6 +17,11 @@ lov/brokyr-thunderkyn lov/hekaton-land-fortress]) +(def kin-bodyguards + [lov/corv + lov/ironkin-assistant + lov/e-cog]) + (def kin-enhancements [{:name "A Long List" :points (Points. "x1" 15)} diff --git a/src/votann/event_handler.clj b/src/votann/event_handler.clj index 8558762..e521724 100644 --- a/src/votann/event_handler.clj +++ b/src/votann/event_handler.clj @@ -1,5 +1,7 @@ (ns votann.event-handler - (:require [votann.codex :refer [kin-models]]) + (:require [votann.codex :refer [kin-models]] + [votann.util :as util] + [votann-battle-simulator.battle-round :refer [shooting-phase fight-phase]]) (:import [javafx.scene.input KeyCode KeyEvent])) (def *state (atom {:list @@ -18,6 +20,29 @@ :tokens "0 " :disabled false}})) +;; TODO find a better place for this +(defn battle-modifiers [state] + (let [judgment-count (clojure.string/trim (get-in @state [:battle-simulator :tokens]))] + (cond + (= "0" judgment-count) + {:hit [] + :wound []} + + (= "1" judgment-count) + {:hit ["Judgment Token"] + :wound []} + + (= "2" judgment-count) + {:hit ["Judgment Token"] + :wound ["Judgment Token"]}))) + +;; TODO find a better place for +(defn total-damage [state] + (doseq [data (get-in @state [:battle-simulator :data])] + (if-not (nil? (get-in @state [:battle-simulator :total-damage (:weapon data)])) + (swap! state assoc-in [:battle-simulator :total-damage (:weapon data)] (conj (get-in @state [:battle-simulator :total-damage (:weapon data)]) (:damage data))) + (swap! state assoc-in [:battle-simulator :total-damage (:weapon data)] [(:damage data)])))) + (defn enter-event [function e] (if (= KeyCode/ENTER (.getCode ^KeyEvent (:fx/event e))) (function e))) @@ -48,32 +73,120 @@ (spit (str (.toString (java.time.Instant/now)) ".list") (:list @*state)))) (defn map-event-handler [e] - (cond (= :event/undo-unit-click (:event/type e)) - (undo-points e) + (cond + ;; list events + (= :event/undo-unit-click (:event/type e)) + (undo-points e) + + (= :event/undo-unit-enter (:event/type e)) + (enter-event undo-points e) + + (= :event/restart-click (:event/type e)) + (restart-list e) + + (= :event/restart-enter (:event/type e)) + (enter-event restart-list e) - (= :event/undo-unit-enter (:event/type e)) - (enter-event undo-points e) + (= :event/remove-unit-click (:event/type e)) + (remove-unit e) - (= :event/restart-click (:event/type e)) - (restart-list e) + (= :event/remove-unit-enter (:event/type e)) + (enter-event remove-unit e) - (= :event/restart-enter (:event/type e)) - (enter-event restart-list e) + (= :event/add-unit-click (:event/type e)) + (add-unit e) - (= :event/remove-unit-click (:event/type e)) - (remove-unit e) + (= :event/add-unit-enter (:event/type e)) + (enter-event add-unit e) - (= :event/remove-unit-enter (:event/type e)) - (enter-event remove-unit e) + (= :event/export-list-click (:event/type e)) + (export-list e) - (= :event/add-unit-click (:event/type e)) - (add-unit e) + (= :event/export-list-enter (:event/type e)) + (enter-event export-list e) - (= :event/add-unit-enter (:event/type e)) - (enter-event add-unit e) + ;;battle-simulator events + (= :event/choice-select-weapon (:event/type e)) + (swap! *state assoc-in [:battle-simulator :weapon-type] (.getValue (.getTarget (:fx/event e)))) - (= :event/export-list-click (:event/type e)) - (export-list e) + (= :event/choice-select-model (:event/type e)) + (do + (swap! *state assoc-in [:battle-simulator :model] (.getValue (.getTarget (:fx/event e)))) + (swap! *state assoc-in [:battle-simulator :count] "0") + (swap! *state assoc-in [:battle-simulator :rolls] "1") + (swap! *state assoc-in [:battle-simulator :data] []) + (swap! *state assoc-in [:battle-simulator :total-damage] {})) + + (= :event/choice-select-reset (:event/type e)) + (do + (swap! *state assoc-in [:battle-simulator :count] "0") + (swap! *state assoc-in [:battle-simulator :data] []) + (swap! *state assoc-in [:battle-simulator :total-damage] {})) - (= :event/export-list-enter (:event/type e)) - (enter-event export-list e))) + (= :event/choice-select-target (:event/type e)) + (do + (swap! *state assoc-in [:battle-simulator :target] (.getValue (.getTarget (:fx/event e)))) + (swap! *state assoc-in [:battle-simulator :count] "0") + (swap! *state assoc-in [:battle-simulator :data] []) + (swap! *state assoc-in [:battle-simulator :total-damage] {})) + + (= :event/choice-select-rolls (:event/type e)) + (try + (let [roll (Integer/parseInt (.getText (.getTarget (:fx/event e))))] + (if (< roll 1) + (do + (swap! *state assoc-in [:battle-simulator :rolls] "") + (.setText (.getTarget (:fx/event e)) "") + (swap! *state assoc-in [:battle-simulator :disabled] true)) + (do + (swap! *state assoc-in [:battle-simulator :rolls] (str roll)) + (swap! *state assoc-in [:battle-simulator :disabled] false)))) + (catch java.lang.NumberFormatException | java.lang.NullPointerException e + (swap! *state assoc-in [:battle-simulator :rolls] "") + (.setText (.getTarget (:fx/event e)) "") + (swap! *state assoc-in [:battle-simulator :disabled] true))) + + (= :event/choice-select-target-size (:event/type e)) + (try + (let [count (Integer/parseInt (.getText (.getTarget (:fx/event e))))] + (if (< count 1) + (do + (swap! *state assoc-in [:battle-simulator :target-count] "") + (.setText (.getTarget (:fx/event e)) "") + (swap! *state assoc-in [:battle-simulator :disabled] true)) + (do + (swap! *state assoc-in [:battle-simulator :target-count] (str count)) + (swap! *state assoc-in [:battle-simulator :disabled] false)))) + (catch java.lang.NumberFormatException | java.lang.NullPointerException e + (swap! *state assoc-in [:battle-simulator :target-count] "") + (.setText (.getTarget (:fx/event e)) "") + (swap! *state assoc-in [:battle-simulator :disabled] true))) + + (= :event/choice-select-tokens (:event/type e)) + (swap! *state assoc-in [:battle-simulator :tokens] (.getValue (.getTarget (:fx/event e)))) + + (= :event/choice-select-stats (:event/type e)) + (swap! *state assoc-in [:battle-simulator :data-type] (.getValue (.getTarget (:fx/event e)))) + + (= :event/choice-select-start (:event/type e)) + (cond (= "Ranged" (get-in @*state [:battle-simulator :weapon-type])) + (do + (swap! *state assoc-in [:battle-simulator :data] + (shooting-phase (Integer/parseInt (get-in @*state [:battle-simulator :rolls])) + (util/get-model (get-in @*state [:battle-simulator :model])) + (Integer/parseInt (get-in @*state [:battle-simulator :target-count])) + (util/get-model (get-in @*state [:battle-simulator :target])) + (battle-modifiers *state))) + (swap! *state assoc-in [:battle-simulator :count] (str (+ (Integer/parseInt (get-in @*state [:battle-simulator :count])) 1))) + (total-damage *state)) + + (= "Melee" (get-in @*state [:battle-simulator :weapon-type])) + (do + (swap! *state assoc-in [:battle-simulator :data] + (fight-phase (Integer/parseInt (get-in @*state [:battle-simulator :rolls])) + (util/get-model (get-in @*state [:battle-simulator :model])) + (Integer/parseInt (get-in @*state [:battle-simulator :target-count])) + (util/get-model (get-in @*state [:battle-simulator :target])) + (battle-modifiers *state))) + (swap! *state assoc-in [:battle-simulator :count] (str (+ (Integer/parseInt (get-in @*state [:battle-simulator :count])) 1))) + (total-damage *state))))) diff --git a/src/votann/tab_widget.clj b/src/votann/tab_widget.clj index 85bb770..4b6cbe7 100644 --- a/src/votann/tab_widget.clj +++ b/src/votann/tab_widget.clj @@ -4,7 +4,7 @@ [votann.unit-widget :refer [unit-view-widget]] [votann.enhancements-widget :refer [enhancements-view-widget]] [votann.codex-view :refer [codex-page]] - [votann.battle-simulator-widget :refer [place-holder]] + [votann.battle-simulator-widget :refer [battle-simulator-widget]] [cljfx.api :as fx] [cljfx.ext.web-view :as fx.ext.web-view])) @@ -34,11 +34,11 @@ :content {:fx/type :tab-pane :tabs unit-view-tab}}]) -(def battle-simulator-view-tab +(defn battle-simulator-view-tab [data] [{:fx/type :tab :text "Battle Simulator" :closable false - :content place-holder}]) + :content (battle-simulator-widget data)}]) (defn tab-widget [data] - (vec (apply concat [(list-view-tab (:list data)) codex-view-tab battle-simulator-view-tab]))) + (vec (apply concat [(list-view-tab (:list data)) codex-view-tab (battle-simulator-view-tab (:battle-simulator data))]))) diff --git a/src/votann/util.clj b/src/votann/util.clj index 307f8a7..bb84624 100644 --- a/src/votann/util.clj +++ b/src/votann/util.clj @@ -1,8 +1,30 @@ (ns votann.util - (:require [cljfx.lifecycle :as fx.lifecycle] + (:require [votann.codex :refer [kin-models kin-bodyguards]] + [cljfx.lifecycle :as fx.lifecycle] [cljfx.component :as fx.component] + [clojure.math :as math] [clojure.string :as string])) +(defn get-model [name] + (->> + (filter #(= (:name %) name) (apply conj kin-models kin-bodyguards)) + first)) + +(defn total [collection] + (apply + collection)) + +(defn mean [collection] + (double (/ (total collection) (count collection)))) + +(defn median [collection] + (let [c (count collection) + m (- (int (math/ceil (double (/ c 2)))) 1) + collection (vec (sort collection))] + (if (= 0 (mod c 2)) + (get collection + (int (math/ceil (double (/ (+ m (+ m 1)) 2))))) + (get collection m)))) + (defn get-resource-path [file] (.toExternalForm (clojure.java.io/resource file))) diff --git a/src/votann_battle_simulator/battle_round.clj b/src/votann_battle_simulator/battle_round.clj new file mode 100644 index 0000000..f5e55b2 --- /dev/null +++ b/src/votann_battle_simulator/battle_round.clj @@ -0,0 +1,87 @@ +(ns votann-battle-simulator.battle-round + (:require [votann-battle-simulator.util :as util] + [votann-battle-simulator.weapon-abilities :as weapon-abilities]) + (:import [votann.records.model Model])) + +(defn resolve-hit-roll [rolls skill] + (vec (filter #(>= % skill) rolls))) + +(defn resolve-wound-roll [rolls strength toughness] + (vec (cond (>= strength (* toughness 2)) + (filter #(>= % 2) rolls) + + (> strength toughness) + (filter #(>= % 3) rolls) + + (= strength toughness) + (filter #(>= % 4) rolls) + + (< strength toughness) + (filter #(>= % 5) rolls) + + (<= strength (double (/ toughness 2))) + (filter #(>= % 6) rolls)))) + +(defn resolve-saving-throw-roll [rolls ap save] + (vec (filter #(< % (+ ap save)) rolls))) + +(defn command-phase []) + +(defn movement-phase []) + +(defn shooting-phase [^Integer unit-size ^Model model ^Integer target-size ^Model target battle-modifiers] + (println "Starting shooting-phase ") + (println (str (:name model) " x" unit-size " target " (:name target) + " W: " (:w target) + " T: " (:t target) + " SV: " (:sv target))) + (for [weapon (:ranged-weapons model)] + (do + (println (str "\nUsing weapon: " (:name weapon))) + (let [rolls (util/roll-d6 (weapon-abilities/resolve-attack-abilities weapon unit-size target-size)) + rolls (weapon-abilities/resolve-hit-dice-abilities weapon rolls) + rolls (weapon-abilities/resolve-hit-modifier-abilities weapon (:hit battle-modifiers) rolls) + hits (resolve-hit-roll rolls (:bs weapon)) + wounds (weapon-abilities/resolve-wound-dice-abilites weapon (util/roll-d6 (count hits)) (:s weapon) (:t target)) + mortal-wounds (count (filter #(= 99 %) wounds)) + wounds (vec (filter #(not= 99 %) wounds)) + wounds (weapon-abilities/resolve-wound-modifier-abilites weapon (:wound battle-modifiers) wounds) + wounds (resolve-wound-roll (util/roll-d6 (count wounds)) (:s weapon) (:t target)) + non-saves (count (resolve-saving-throw-roll (util/roll-d6 (count wounds)) (:ap weapon) (:sv target))) + damage (* (util/resolve-stat-count (:d weapon)) (+ mortal-wounds non-saves))] + (println (str "Total damage: " damage)) + {:dialog (str (:name model) " x" unit-size " target " (:name target) + " W: " (:w target) + " T: " (:t target) + " SV: " (:sv target)) + :weapon (:name weapon) :damage damage} + )))) + +(defn charge-phase []) + +(defn fight-phase [^Integer unit-size ^Model model ^Integer target-size ^Model target battle-modifiers] + (println "Starting fight-phase ") + (println (str (:name model) " x" unit-size " target " (:name target) + " W: " (:w target) + " T: " (:t target) + " SV: " (:sv target))) + (for [weapon (:melee-weapons model)] + (do + (println (str "\nUsing weapon: " (:name weapon))) + (let [rolls (util/roll-d6 (weapon-abilities/resolve-attack-abilities weapon unit-size target-size)) + rolls (weapon-abilities/resolve-hit-dice-abilities weapon rolls) + rolls (weapon-abilities/resolve-hit-modifier-abilities weapon (:hit battle-modifiers) rolls) + hits (resolve-hit-roll rolls (:bs weapon)) + wounds (weapon-abilities/resolve-wound-dice-abilites weapon (util/roll-d6 (count hits)) (:s weapon) (:t target)) + mortal-wounds (count (filter #(= 99 %) wounds)) + wounds (vec (filter #(not= 99 %) wounds)) + wounds (weapon-abilities/resolve-wound-modifier-abilites weapon (:wound battle-modifiers) wounds) + wounds (resolve-wound-roll (util/roll-d6 (count wounds)) (:s weapon) (:t target)) + non-saves (count (resolve-saving-throw-roll (util/roll-d6 (count wounds)) (:ap weapon) (:sv target))) + damage (* (util/resolve-stat-count (:d weapon)) (+ mortal-wounds non-saves))] + (println (str "Total damage: " damage)) + {:dialog (str (:name model) " x" unit-size " target " (:name target) + " W: " (:w target) + " T: " (:t target) + " SV: " (:sv target)) + :weapon (:name weapon) :damage damage})))) diff --git a/src/votann_battle_simulator/util.clj b/src/votann_battle_simulator/util.clj new file mode 100644 index 0000000..1292bf2 --- /dev/null +++ b/src/votann_battle_simulator/util.clj @@ -0,0 +1,46 @@ +(ns votann-battle-simulator.util) + +(defn roll-d6 [n] + (loop [i 0 + rolls ()] + (if (>= i n) + (vec rolls) + (recur (+ i 1) (conj rolls (+ 1 (rand-int 6))))))) + +(defn roll-d3 [n] + (mapv #(int (Math/ceil (double (/ % 2)))) (roll-d6 n))) + +(defn d+ [n rolls] + (vec (filter #(>= % n) rolls))) + +(defn resolve-stat-count [stat] + (if (int? stat) + stat + (let [add-num (re-find #"\+\d+" stat) + num (if (nil? add-num) + 0 + (Integer/parseInt add-num))] + (cond + (re-find #"D6" stat) + (+ num (first (roll-d6 1))) + + (re-find #"D3" stat) + (+ num (first (roll-d3 1))))))) + +(defn modifier-cap [modifier] + (cond + (< modifier 0) -1 + (> modifier 0) 1 + :else 0)) + +(defn dice-cap [dice] + (cond + (< dice 1) 1 + (> dice 6) 6 + :else dice)) + +(defn mod+1 [modifier] + (+ modifier 1)) + +(defn mod-1 [modifier] + (- modifier 1)) diff --git a/src/votann_battle_simulator/weapon_abilities.clj b/src/votann_battle_simulator/weapon_abilities.clj new file mode 100644 index 0000000..a0b37b5 --- /dev/null +++ b/src/votann_battle_simulator/weapon_abilities.clj @@ -0,0 +1,185 @@ +(ns votann-battle-simulator.weapon-abilities + (:require [votann-battle-simulator.util :as util]) + (:import [votann.records.weapon Weapon])) + +(defn blast + ([target-size] + (int (/ target-size 5))) + ([dice unit-size target-size] + (let [blast-added (+ dice (* unit-size (blast target-size)))] + (println "Dice count before " dice " after " blast-added) + blast-added))) + +(defn devastating-wounds [dice] + (let [mortal-added (vec (map #(if (= 6 %) + 99 + %) dice))] + (println "Hit rolls before " dice " after " mortal-added) + mortal-added)) + +(defn twin-linked [dice strength toughness] + (let [re-rolls (vec (cond (>= strength (* toughness 2)) + (filter #(< % 2) dice) + + (> strength toughness) + (filter #(< % 3) dice) + + (= strength toughness) + (filter #(< % 4) dice) + + (< strength toughness) + (filter #(< % 5) dice) + + (<= strength (double (/ toughness 2))) + (filter #(< % 6) dice))) + dice-rolls (vec (filter (complement (set re-rolls)) dice)) + twin-linked-rolls (apply conj dice-rolls (util/roll-d6 (count re-rolls)))] + (println "Wounds before " dice " after " twin-linked-rolls) + twin-linked-rolls)) + +(defn rapid-fire [dice modifier] + (cond + (re-find #"D\d" modifier) + (let [rapid-fire-modifier (util/resolve-stat-count modifier) + rapid-fire-added (+ dice rapid-fire-modifier)] + (println "Dice count before " dice " after " rapid-fire-added) + rapid-fire-added) + :else + (let [rapid-fire-added (+ dice (Integer/parseInt (re-find #"\d+" modifier)))] + (println "Dice count before " dice " after " rapid-fire-added) + rapid-fire-added))) + +(defn sustained-hits [modifier dice] + (let [critical (count (filter #(= 6 %) dice))] + (cond + (re-find #"D\d" modifier) + (let [crit-added (apply conj dice + (vec (repeat (* + critical + (util/resolve-stat-count modifier)) + 7)))] + (println "Hit count before " dice " after " crit-added) + crit-added) + + :else + (let [crit-added (apply conj dice + (vec (repeat (* + critical + (Integer/parseInt (re-find #"\d+" modifier))) + 7)))] + (println "Hit count after " dice " after " crit-added) + crit-added) + ))) + +(defn resolve-attack-abilities [^Weapon weapon unit-size target-size] + (loop [abilities (:abilities weapon) + dice (* unit-size (util/resolve-stat-count (:a weapon)))] + (if (empty? abilities) + dice + (let [ability (first abilities)] + (cond + (not (nil? (re-find #"Blast" ability))) + (do + (println "Applied Blast attack ability for " (:name weapon)) + (recur (rest abilities) + (blast dice unit-size target-size))) + + (not (nil? (re-find #"Rapid Fire" ability))) + (do + (println "Applied Rapid fire attack ability for " (:name weapon)) + (recur (rest abilities) + (rapid-fire dice ability))) + + :else (recur (rest abilities) + dice)))))) + +(defn resolve-hit-dice-abilities [^Weapon weapon dice] + (loop [abilities (:abilities weapon) + dice-rolls dice] + (if (empty? abilities) + dice-rolls + (let [ability (first abilities)] + (cond + (not (nil? (re-find #"Sustained Hits" ability))) + (do + (println "Applied Sustained Hits hit ability for " (:name weapon)) + (recur (rest abilities) + (sustained-hits ability dice-rolls))) + + :else + (recur (rest abilities) + dice-rolls)))))) + +(defn resolve-hit-modifier-abilities [^Weapon weapon modifier-abilities dice] + (loop [abilities (apply conj (:abilities weapon) modifier-abilities) + modifier 0] + (if (empty? abilities) + (let [modifier-added (vec (map #(if (or (= 1 %) (= 6 %)) + % + (util/dice-cap (+ (util/modifier-cap modifier) %))) dice))] + (println "Resolve hit Before " dice " after " modifier-added) + modifier-added) + (let [ability (first abilities)] + (cond + (not (nil? (re-find #"Heavy" ability))) + (do + (println "Applied Heavy hit modifier for " (:name weapon)) + (recur (rest abilities) + (util/mod+1 modifier))) + + (not (nil? (re-find #"Judgment Token" ability))) + (do + (println "Applied Judgment Token hit modifier for " (:name weapon)) + (recur (rest abilities) + (util/mod+1 modifier))) + :else + (recur (rest abilities) + modifier)))))) + +(defn resolve-wound-dice-abilites [^Weapon weapon dice strength toughness] + (loop [abilities (:abilities weapon) + dice-rolls dice + modifier 0] + (if (empty? abilities) + dice-rolls + (let [ability (first abilities)] + (cond + (not (nil? (re-find #"Devastating Wounds" ability))) + (do + (println "Applied Devastating Wounds wound ability for " (:name weapon)) + (recur (rest abilities) + (devastating-wounds dice) + modifier)) + + (not (nil? (re-find #"Twin-Linked" ability))) + (do + (println "Twin-Linked wound ability for " (:name weapon)) + (recur (rest abilities) + (twin-linked dice strength toughness) + modifier)) + + :else + (recur (rest abilities) + dice-rolls + modifier)))))) + +(defn resolve-wound-modifier-abilites [^Weapon weapon modifier-abilities dice] + (loop [abilities (apply conj (:abilities weapon) modifier-abilities) + modifier 0] + (if (empty? abilities) + (let [modifier-added (vec (map #(if (or (= 1 %) (= 6 %)) + % + (util/dice-cap (+ (util/modifier-cap modifier) %))) dice))] + (println "Resolve wound Before " dice " after " modifier-added) + modifier-added) + (let [ability (first abilities)] + (cond + (not (nil? (re-find #"Judgment Token" ability))) + (do + (println "Applied Judgment Token wound modifier for " (:name weapon)) + (recur (rest abilities) + (util/mod+1 modifier))) + + :else + (recur (rest abilities) + modifier)))))) |