diff options
| author | 0xNul <57599792+0xNul@users.noreply.github.com> | 2023-07-22 13:11:54 -0700 |
|---|---|---|
| committer | 0xNul <57599792+0xNul@users.noreply.github.com> | 2023-07-22 13:11:54 -0700 |
| commit | 9c601ef72f6c44e73c862f4efed0041463b8a257 (patch) | |
| tree | a2bd678da0e0d8ebea170c2ea77bd15c2d2c5f28 /src/votann_battle_simulator | |
| parent | bf1f9ed2f636b6374e32c8261d06f9942a064044 (diff) | |
added battle simulator
Diffstat (limited to 'src/votann_battle_simulator')
| -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 |
3 files changed, 318 insertions, 0 deletions
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)))))) |