diff options
| -rw-r--r-- | .gitignore | 15 | ||||
| -rw-r--r-- | README.md | 49 | ||||
| -rw-r--r-- | doc/intro.md | 1 | ||||
| -rw-r--r-- | project.clj | 12 | ||||
| -rw-r--r-- | src/votann/codex.clj | 44 | ||||
| -rw-r--r-- | src/votann/core.clj | 29 | ||||
| -rw-r--r-- | src/votann/current_list_widget.clj | 52 | ||||
| -rw-r--r-- | src/votann/enhancements_widget.clj | 11 | ||||
| -rw-r--r-- | src/votann/event_handler.clj | 54 | ||||
| -rw-r--r-- | src/votann/list_widget.clj | 15 | ||||
| -rw-r--r-- | src/votann/point_chart_widget.clj | 17 | ||||
| -rw-r--r-- | src/votann/tab_widget.clj | 23 | ||||
| -rw-r--r-- | src/votann/unit_widget.clj | 12 | ||||
| -rw-r--r-- | src/votann/units_and_points_widget.clj | 38 | ||||
| -rw-r--r-- | src/votann/util.clj | 48 | ||||
| -rw-r--r-- | test/votann/core_test.clj | 7 |
16 files changed, 427 insertions, 0 deletions
diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..0729ff2 --- /dev/null +++ b/.gitignore @@ -0,0 +1,15 @@ +/target +/classes +/checkouts +profiles.clj +pom.xml +pom.xml.asc +*.jar +*.class +/.lein-* +/.nrepl-port +/.prepl-port +.hgignore +.hg/ +*~ +/resources/leagues-of-votann/* diff --git a/README.md b/README.md new file mode 100644 index 0000000..83054ce --- /dev/null +++ b/README.md @@ -0,0 +1,49 @@ +# Votann + +Votann is an informational program for creating your unit lists for Warhammer 40k. + +## Installation + +Download votann-0.1.0.jar + +## Compile from source + +1. Install [clojure](https://clojure.org/guides/install_clojure), openJDK, [leinagain](https://leiningen.org). + +2. `run lein uberjar` (might need to `run lein deps` before if it doesnt do it automatically) + +3. `java -jar target/uberjar/votann-0.1.0-standalone.jar` + +(Please note I did not include the images for these, so if you build from source you will not have the unit and enhancements tabs viewable reach out to me and I can provide those files until I create non-copyright versions) + +## Usage + +Navigate with clicking or tab or arrow keys and enter to select. + +### Macos/Linux + +Run the following command in your terminal or create a `.sh` script. + +`$ java -jar votann-0.1.0.jar` + +### Windows + +You can create a `.bat` file with the above command. Set the absolute path of the jar file or place the `.bat` file in the same directory as the `.jar` file. Or run the above command in your terminal. + +## License + +Votann is an informational program for creating your unit lists for Warhammer 40k +Copyright (C) 2023 OxNull + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see <https://www.gnu.org/licenses/>. diff --git a/doc/intro.md b/doc/intro.md new file mode 100644 index 0000000..f3d27c7 --- /dev/null +++ b/doc/intro.md @@ -0,0 +1 @@ +# Introduction to votann diff --git a/project.clj b/project.clj new file mode 100644 index 0000000..69a11d4 --- /dev/null +++ b/project.clj @@ -0,0 +1,12 @@ +(defproject votann "0.1.0" + :description "Votann is an informational program for creating your unit lists for Warhammer 40k" + :url "https://github.com/0xNul/votann" + :license {:name "GPL-3.0-or-later" + :url "https://www.gnu.org/licenses/gpl-3.0.en.html"} + :dependencies [[org.clojure/clojure "1.11.1"] + [cljfx "1.7.23"] + [org.openjfx/javafx-base "19"]] + :main ^:skip-aot votann.core + :target-path "target/%s" + :profiles {:uberjar {:aot :all + :jvm-opts ["-Dcljfx.skip-javafx-initialization=true"]}}) diff --git a/src/votann/codex.clj b/src/votann/codex.clj new file mode 100644 index 0000000..14d8ddf --- /dev/null +++ b/src/votann/codex.clj @@ -0,0 +1,44 @@ +(ns votann.codex) + +(defrecord Unit [count points]) + +(def kin-models + [{:name "Uthar The Destined" + :units [(Unit. "x1" 115)]} + {:name "Kahl" + :units [(Unit. "x1" 90)]} + {:name "Einhyr Champion" + :units [(Unit. "x1" 75)]} + {:name "Grimnyr" + :units [(Unit. "x3" 75)]} + {:name "Brokhyr Iron-Master" + :units [(Unit. "x3" 95) + (Unit. "x6" 190)]} + {:name "Hearthkyn Warriors" + :units [(Unit. "x10" 135)]} + {:name "Einhyr Hearthguard" + :units [(Unit. "x5" 165) + (Unit. "x10" 330)]} + {:name "Cthonian Beserks" + :units [(Unit. "x5" 135) + (Unit. "x10" 270)]} + {:name "Hernkyn Pioneers" + :units [(Unit. "x3" 105) + (Unit. "x6" 210)]} + {:name "Sagitaur" + :units [(Unit. "x1" 120)]} + {:name "Brokhyr Thunderkyn" + :units [(Unit. "x3" 95) + (Unit. "x6" 190)]} + {:name "Hekaton Land Fortress" + :units [(Unit. "x1" 245)]}]) + +(def kin-enhancements + [{:name "A Long List" + :units [(Unit. "x1" 15)]} + {:name "Apprasing Glare" + :units [(Unit. "x1" 20)]} + {:name "Grim Demeanour" + :units [(Unit. "x1" 20)]} + {:name "Wayfarers Grace" + :units [(Unit. "x1" 20)]}]) diff --git a/src/votann/core.clj b/src/votann/core.clj new file mode 100644 index 0000000..50322eb --- /dev/null +++ b/src/votann/core.clj @@ -0,0 +1,29 @@ +(ns votann.core + (:gen-class) + (:require [votann.util :refer [get-resource-path]] + [votann.event-handler :refer [map-event-handler *list-units]] + [votann.tab_widget :refer [tab-widget]] + [cljfx.api :as fx])) + +(defn root-view [data] + {:fx/type :stage + :max-width 1253 + :max-height 860 + :icons [(get-resource-path (str "leagues-of-votann/icon256x256.png")) + (get-resource-path (str "leagues-of-votann/icon48x48.png")) + (get-resource-path (str "leagues-of-votann/icon32x32.png"))] + :showing true + :title "Votann" + :scene {:fx/type :scene + :root {:fx/type :stack-pane + :children [{:fx/type :tab-pane + :tabs (tab-widget data)}]}}}) + +(def renderer + (fx/create-renderer + :middleware (fx/wrap-map-desc assoc :fx/type root-view) + :opts {:fx.opt/map-event-handler map-event-handler})) + +(defn -main + [& args] + (fx/mount-renderer *list-units renderer)) diff --git a/src/votann/current_list_widget.clj b/src/votann/current_list_widget.clj new file mode 100644 index 0000000..9f54f0a --- /dev/null +++ b/src/votann/current_list_widget.clj @@ -0,0 +1,52 @@ +(ns votann.current-list-widget + (:require [cljfx.api :as fx])) + +(defn current-list [data] + (vec (for [[index unit] (map-indexed vector data)] + {:fx/type :v-box + :padding 5 + :children [{:fx/type :label + :text (:name unit)} + {:fx/type :h-box + :alignment :center-left + :spacing 5 + :children [{:fx/type :button + :on-mouse-clicked {:event/type :event/remove-unit-click + :event/target {:index index + :points (:points unit)}} + :on-key-pressed {:event/type :event/remove-unit-enter + :event/target {:index index + :points (:points unit)}} + :text "Remove"} + {:fx/type :label + :text (str (:points unit) "pts")}]}]}))) + +(defn current-list-label-widget [{:keys [points]}] + {:fx/type :v-box + :alignment :center + :children [ + {:fx/type :label + :style "-fx-font-size: 18px; -fx-font-weight: bold;" + :text "Current List"} + {:fx/type :h-box + :alignment :center + :spacing 50 + :children [{:fx/type :button + :on-mouse-clicked {:event/type :event/restart-click} + :on-key-pressed {:event/type :event/restart-enter} + :text "Restart"} + {:fx/type :label + :style "-fx-font-size: 18px; -fx-font-weight: bold;" + :text (str points " pts")} + {:fx/type :button + :on-mouse-clicked {:event/type :event/undo-unit-click} + :on-key-pressed {:event/type :event/undo-unit-enter} + :text "Undo"}]} + ]}) + +(defn current-list-widget [{:keys [units]}] + {:fx/type :scroll-pane + :content {:fx/type :v-box + :alignment :top-center + :pref-height 600 + :children (current-list units)}}) diff --git a/src/votann/enhancements_widget.clj b/src/votann/enhancements_widget.clj new file mode 100644 index 0000000..65a99a2 --- /dev/null +++ b/src/votann/enhancements_widget.clj @@ -0,0 +1,11 @@ +(ns votann.enhancements-widget + (:require [votann.util :refer [get-resource-path]] + [cljfx.api :as fx])) + +(def enhancements-view-widget + {:fx/type :scroll-pane + :content {:fx/type :h-box + :children [{:fx/type :image-view + :fit-width 1233 + :preserve-ratio true + :image (get-resource-path (str "leagues-of-votann/enhancements.png"))}]}}) diff --git a/src/votann/event_handler.clj b/src/votann/event_handler.clj new file mode 100644 index 0000000..89d9795 --- /dev/null +++ b/src/votann/event_handler.clj @@ -0,0 +1,54 @@ +(ns votann.event-handler + (:import [javafx.scene.input KeyCode KeyEvent])) + +(def *list-units (atom {:points 0 :units []})) + +(defn enter-event [function e] + (if (= KeyCode/ENTER (.getCode ^KeyEvent (:fx/event e))) + (function e))) + +(defn undo-points [_] + (if-not (empty? (get-in @*list-units [:units])) + (let [points (:points (last (:units @*list-units)))] + (swap! *list-units assoc :points (max 0 (- (:points @*list-units) points))) + (swap! *list-units update-in [:units] pop)))) + +(defn restart-list [_] + (if-not (empty? (get-in @*list-units [:units])) + (do + (swap! *list-units assoc :points 0) + (swap! *list-units assoc-in [:units] [])))) + +(defn add-unit [e] + (swap! *list-units assoc :points (+ (:points @*list-units) (:points (:event/target e)))) + (swap! *list-units update-in [:units] conj (:event/target e))) + +(defn remove-unit [e] + (do + (swap! *list-units assoc :points (max 0 (- (:points @*list-units) (:points (:event/target e))))) + (swap! *list-units update :units #(vec (concat (subvec % 0 (:index (:event/target e))) (subvec % (inc (:index (:event/target e))))))))) + +(defn map-event-handler [e] + (cond (= :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/remove-unit-click (:event/type e)) + (remove-unit e) + + (= :event/remove-unit-enter (:event/type e)) + (enter-event remove-unit e) + + (= :event/add-unit-click (:event/type e)) + (add-unit e) + + (= :event/add-unit-enter (:event/type e)) + (enter-event add-unit e))) diff --git a/src/votann/list_widget.clj b/src/votann/list_widget.clj new file mode 100644 index 0000000..efd751e --- /dev/null +++ b/src/votann/list_widget.clj @@ -0,0 +1,15 @@ +(ns votann.list-widget + (:require [votann.point-chart-widget :refer [point-chart-widget]] + [votann.units-and-points-widget :refer [unit-and-points-widget]] + [votann.current-list-widget :refer [current-list-label-widget current-list-widget]] + [cljfx.api :as fx])) + +(defn left-widget [data] + {:fx/type :v-box + :alignment :center + :children [(point-chart-widget data) (current-list-label-widget data) (current-list-widget data)]}) + +(defn list-view-widget [data] + {:fx/type :h-box + :alignment :center + :children [(left-widget data) unit-and-points-widget]}) diff --git a/src/votann/point_chart_widget.clj b/src/votann/point_chart_widget.clj new file mode 100644 index 0000000..c4ac331 --- /dev/null +++ b/src/votann/point_chart_widget.clj @@ -0,0 +1,17 @@ +(ns votann.point-chart-widget + (:require [cljfx.api :as fx] + [votann.util :refer [ext-recreate-on-key-changed]])) + +(defn chart-data [units] + (for [unit (set units)] + {:fx/type :pie-chart-data + :name (:name unit) + :pie-value (* (:points unit) (count (filter #(= (:name %) (:name unit)) units)))})) + +(defn point-chart-widget [{:keys [units]}] + {:fx/type ext-recreate-on-key-changed + :key units + :desc {:fx/type :pie-chart + :pref-height 600 + :title "Point Distribution" + :data (chart-data units)}}) diff --git a/src/votann/tab_widget.clj b/src/votann/tab_widget.clj new file mode 100644 index 0000000..ab6a8ae --- /dev/null +++ b/src/votann/tab_widget.clj @@ -0,0 +1,23 @@ +(ns votann.tab_widget + (:require [votann.list-widget :refer [list-view-widget]] + [votann.util :refer [get-models unit-file-name]] + [votann.unit-widget :refer [unit-view-widget]] + [votann.enhancements-widget :refer [enhancements-view-widget]] + [cljfx.api :as fx])) + +(defn tab-widget [data] + (vec (apply merge + (vec (apply merge + [{:fx/type :tab + :text "List" + :closable false + :content (list-view-widget data)}] + (vec (for [unit get-models] + {:fx/type :tab + :text unit + :closable false + :content (unit-view-widget (unit-file-name unit))})))) + [{:fx/type :tab + :text "Enhancements" + :closable false + :content enhancements-view-widget}]))) diff --git a/src/votann/unit_widget.clj b/src/votann/unit_widget.clj new file mode 100644 index 0000000..5d31b99 --- /dev/null +++ b/src/votann/unit_widget.clj @@ -0,0 +1,12 @@ +(ns votann.unit-widget + (:require [votann.util :refer [get-resource-path]] + [cljfx.api :as fx])) + +(defn unit-view-widget [unit-file-name] + {:fx/type :scroll-pane + :content {:fx/type :v-box + :alignment :center + :children [{:fx/type :image-view + :image (get-resource-path (str "leagues-of-votann/" unit-file-name "-front.png"))} + {:fx/type :image-view + :image (get-resource-path (str "leagues-of-votann/" unit-file-name "-back.png"))}]}}) diff --git a/src/votann/units_and_points_widget.clj b/src/votann/units_and_points_widget.clj new file mode 100644 index 0000000..48018dd --- /dev/null +++ b/src/votann/units_and_points_widget.clj @@ -0,0 +1,38 @@ +(ns votann.units-and-points-widget + (:require [votann.util :refer [get-units]] + [cljfx.api :as fx])) + + +(def units-points-label-widget + {:fx/type :label + :style "-fx-font-size: 18px; -fx-font-weight: bold;" + :text "Units & Points"}) + +(def unit-points-list-widget + (vec (for [unit get-units] + {:fx/type :v-box + :padding 5 + :children [{:fx/type :label + :text (:name unit)} + {:fx/type :h-box + :alignment :center-left + :spacing 5 + :children [{:fx/type :button + :on-mouse-clicked {:event/type :event/add-unit-click + :event/target {:name (:name unit) + :points (:points unit)}} + :on-key-pressed {:event/type :event/add-unit-enter + :event/target {:name (:name unit) + :points (:points unit)}} + :text "Add"} + {:fx/type :label + :text (str (:points unit) " pts")}]}]}))) + +(def unit-and-points-widget + {:fx/type :scroll-pane + :fit-to-width true + :min-width 175 + :content + {:fx/type :v-box + :alignment :center + :children (vec (apply merge [units-points-label-widget] unit-points-list-widget))}}) diff --git a/src/votann/util.clj b/src/votann/util.clj new file mode 100644 index 0000000..2706a58 --- /dev/null +++ b/src/votann/util.clj @@ -0,0 +1,48 @@ +(ns votann.util + (:require [votann.codex :refer [kin-models kin-enhancements]] + [cljfx.lifecycle :as fx.lifecycle] + [cljfx.component :as fx.component] + [clojure.string :as string])) + +(def get-models + (vec (for [model kin-models] + (:name model)))) + +(def get-units + (->> (concat kin-models kin-enhancements) + (map #(for [unit (:units %)] + {:name (str (:name %) " " (:count unit)) + :points (:points unit)})) + (apply concat) + vec)) + +(defn get-resource-path [file] + (.toExternalForm (clojure.java.io/resource file))) + +(comment get-local-path) + +(defn unit-file-name [unit] + (string/lower-case (string/replace unit " " "-"))) + +(defn remove-by-index [data index] + (vec (concat (subvec data 0 index) + (subvec data (inc index))))) + +(def ext-recreate-on-key-changed + "Extension lifecycle that recreates its component when lifecycle's key is changed + + Supported keys: + - `:key` (required) - a value that determines if returned component should be recreated + - `:desc` (required) - a component description with additional lifecycle semantics" + (reify fx.lifecycle/Lifecycle + (create [_ {:keys [key desc]} opts] + (with-meta {:key key + :child (fx.lifecycle/create fx.lifecycle/dynamic desc opts)} + {`fx.component/instance #(-> % :child fx.component/instance)})) + (advance [this component {:keys [key desc] :as this-desc} opts] + (if (= (:key component) key) + (update component :child #(fx.lifecycle/advance fx.lifecycle/dynamic % desc opts)) + (do (fx.lifecycle/delete this component opts) + (fx.lifecycle/create this this-desc opts)))) + (delete [_ component opts] + (fx.lifecycle/delete fx.lifecycle/dynamic (:child component) opts)))) diff --git a/test/votann/core_test.clj b/test/votann/core_test.clj new file mode 100644 index 0000000..362530e --- /dev/null +++ b/test/votann/core_test.clj @@ -0,0 +1,7 @@ +(ns votann.core-test + (:require [clojure.test :refer :all] + [votann.core :refer :all])) + +(deftest a-test + (testing "FIXME, I fail." + (is (= 0 1)))) |