(ns advent.actions (:require [play-clj.core :refer :all] [play-clj.ui :refer :all] [play-clj.utils :refer :all] [play-clj.g2d :refer :all] [clojure.pprint] [clojure.string :as s] [advent.pathfind] [advent.actions :as actions] [advent.screens.dialogue :as dialogue] [clojure.core.async :refer [put! ! >!! chan go thread take! alts!!]]) (:import [com.badlogic.gdx.graphics Pixmap Pixmap$Filter Texture Texture$TextureFilter] [com.badlogic.gdx.graphics.g2d TextureRegion] [com.badlogic.gdx Screen])) (defprotocol IAction (begin [this screen entities]) (done? [this screen entities]) (continue [this screen entities]) (terminate [this screen entities]) (get-channel [this])) (defmacro get-script [entities & forms] `(fn [starting-entities#] (let [~entities (atom starting-entities#)] (thread ~@forms)))) (defn jump-to [screen entities entity [x y]] (let [scale-fn (-> entities :background :scale-fn) entity (assoc entity :x x :y y :baseline (- 240 y))] (if (:scaled entity) (assoc entity :scale-x (scale-fn y) :scale-y (scale-fn y)) entity))) (defn start-animation [screen entity anim] (let [new-anim (if (keyword? anim) (or (get-in entity [(:facing entity) anim]) (anim entity)) anim)] (if (and anim (not= new-anim (:anim entity))) (assoc entity :anim new-anim :anim-start (:total-time screen)) entity))) (defn stop [screen entities target-id] (update-in entities [:background :entities target-id] #(start-animation screen % :stand))) (defn dist [x1 y1 x2 y2] (let [dx (- x1 x2) dy (- y1 y2)] (Math/sqrt (+ (* dx dx) (* dy dy))))) (defmacro run-action [entities & forms] `(let [c# (chan)] (do (put! (get-in (deref ~entities) [:actions :channel]) (reify IAction (get-channel [_] c#) ~@forms)) (reset! ~entities ( entities (assoc-in [:background :entities target-id :x] target-x) (assoc-in [:background :entities target-id :y] target-y))) (update-in entities [:background :entities target-id] #(start-animation screen (assoc (jump-to screen entities % [(+ moved-x from-x) (+ moved-y from-y)]) :facing (cond (< delta-x 0) :left (> delta-x 0) :right :else (:facing %))) :walk )))))) (done? [this screen entities] (let [{from-x :x from-y :y :keys [left right anim] :as target-entity} (get-in entities [:background :entities target-id])] (< (dist final-x final-y from-x from-y) 1))) (terminate [this screen entities] (stop screen entities target-id))) @entities))) (defn get-text-duration [text] (* (count (s/split text #" ")) 0.5)) (defn talk [entities target-id text & {:keys [stop?]}] (let [initial-time (atom nil) stop? (if (nil? stop?) true stop?)] (run-action entities (begin [this screen entities] (let [_ (swap! initial-time #(or % (:total-time screen))) target-y (get-in entities [:background :entities target-id :y]) scale-fn (get-in entities [:background :scale-fn]) scale (scale-fn target-y) height (* scale 36)] (run! dialogue/talking-screen :on-talk :text text :x (get-in entities [:background :entities target-id :x]) :y (+ (get-in entities [:background :entities target-id :y]) height) :target-id target-id :scale scale) (update-in entities [:background :entities target-id ] #(start-animation screen % :talk)))) (continue [this screen entities] entities) (done? [this screen entities] (> (- (:total-time screen) @initial-time) (get-text-duration text))) (terminate [this screen entities] (run! dialogue/talking-screen :stop-talk :target-id target-id) (if stop? (stop screen entities target-id) entities))))) (defn present-choices [entities & pairs] (run-action entities (begin [this screen entities] (run! dialogue/choice-screen :on-present-choices :pairs pairs) (run! @(resolve 'advent.screens.scene/scene) :on-deactivate) entities) (continue [this screen entities] entities) (done? [this screen entities] true) (terminate [this screen entities] entities))) (defn give [entities target-id item] (run-action entities (begin [this screen entities] (sound! (sound "pickup.mp3") :play) (-> entities (update-in [:background :entities target-id :inventory] #(conj % item)) (assoc-in [:cursor :current] item))) (continue [this screen entities] entities) (done? [this screen entities] true) (terminate [this screen entities] entities))) (defn transition-background [entities new-background [x y]] (run-action entities (begin [this screen entities] (-> entities (assoc-in [:transition] (assoc (texture "black.png") :scale-x 20 :scale-y 20 :baseline 9500 :opacity 0.1)))) (continue [this screen entities] (update-in entities [:transition :opacity] + 0.075)) (done? [this screen entities] (>= (get-in entities [:transition :opacity]) 1.0)) (terminate [this screen entities] entities)) (run-action entities (begin [this screen entities] (let [ego (get-in entities [:background :entities :ego]) entities (-> entities (assoc-in [:background] (get-in entities [:backgrounds new-background])) (assoc-in [:background :entities :ego] ego))] (-> entities (update-in [:background :entities :ego] #(jump-to screen entities % [x y]))))) (continue [this screen entities] (update-in entities [:transition :opacity] - 0.075)) (done? [this screen entities] (<= (get-in entities [:transition :opacity]) 0.0)) (terminate [this screen entities] (dissoc entities :transition))))