(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.string :as s] [clojure.zip :as zip] [clojure.set :as set] [clojure.tools.logging :as log] [advent.pathfind] [advent.actions :as actions] [advent.screens.dialogue :as dialogue] [advent.utils :as utils] [advent.screens.rooms :as rooms] [advent.tween :as tween] [advent.tween :as tween] [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 Animation] [com.badlogic.gdx Screen])) (defprotocol IAction (begin [this screen entities]) (done? [this screen entities]) (continue [this screen entities]) (terminate [this screen entities]) (skip-type [this screen entities]) (get-channel [this])) (defn has-item? [entities item] (if (map? entities) ((set (get-in entities [:state :inventory])) item) ((set (get-in @entities [:state :inventory])) item))) (defn has-obtained? [entities item] (if (map? entities) ((get-in entities [:state :obtained-items]) item) ((get-in @entities [:state :obtained-items]) item))) (defn has-obtained-one-of? [entities items] (some (partial has-obtained? entities) items)) (defn has-obtained-all-of? [entities items] (every? (partial has-obtained? entities) items)) (defn has-one-of? [entities items] (if (map? entities) (seq (set/intersection (set (get-in entities [:state :inventory])) (set items))) (seq (set/intersection (set (get-in @entities [:state :inventory])) (set items))))) (def ^{:dynamic true} *fg-bg-key* :fg-actions) (defmacro run-action [entities & forms] `(let [c# (chan)] (do (put! (get-in (deref ~entities) [*fg-bg-key* :channel]) (reify IAction (get-channel [_] c#) ~@forms)) (let [result# ( entities (update-in [fg-bg-key] assoc :script-running? state :last-skip-type (if state :end nil)) (assoc-in [:cursor :came-from-inventory?] false))) (continue [this screen entities] entities) (done? [this screen entities] true) (terminate [this screen entities] entities) (skip-type [this screen entities] :none-but-arrow)))) (defmacro get-bg-script [entities & forms] `(fn [starting-entities#] (put! (get-in starting-entities# [:bg-actions :script-chan]) (fn [starting-entities#] (let [~entities (atom starting-entities#)] (thread (binding [*fg-bg-key* :bg-actions] ~@forms (change-script-state ~entities false) ))))))) (defmacro get-script [entities & forms] `(fn [starting-entities#] (put! (get-in starting-entities# [:fg-actions :script-chan]) (fn [starting-entities#] (let [~entities (atom starting-entities#)] (thread (try (binding [*fg-bg-key* :fg-actions] ~@forms (change-script-state ~entities false) (utils/save @~entities "autosave" "Autosave")) (catch clojure.lang.ExceptionInfo e# (println "caught exception" e#)) (catch Exception e# (println "Caught exception") (.printStackTrace e#))))))))) (defn force-end [entities current-action key] (do (when current-action (put! (get-channel current-action) :end)) (screen! dialogue/talking-screen :stop-talk {:id key}) (-> entities (assoc-in [key :script-running?] false) (assoc-in [key :current] nil) (assoc-in [key :started?] false) (assoc-in [key :channel] (chan))))) (defmacro get-unsaved-script [entities & forms] `(fn [starting-entities#] (put! (get-in starting-entities# [:fg-actions :script-chan]) (fn [starting-entities#] (let [~entities (atom starting-entities#)] (thread (binding [*fg-bg-key* :fg-actions] ~@forms (change-script-state ~entities false)))))))) (defn pan-to [screen entities x y scale-fn & [ease duration]] (let [ease (or ease tween/ease-in-out-quadratic) duration (or duration 3.0) target-zoom (min utils/min-zoom (max utils/max-zoom (scale-fn [(max 0 (min 319 x)) (max 0 (min 240 y))]))) current-zoom (get-in entities [:cam :zoom] 1.0) ;; don't zoom if it's a subtle difference target-zoom (if (< (Math/abs (double (- target-zoom current-zoom))) 0.07) current-zoom target-zoom) target-x (utils/bound-to-camera x 320 target-zoom) target-y (utils/bound-to-camera y 240 target-zoom)] (if (and (or (not= target-x (get-in entities [:cam :x])) (not= target-y (get-in entities [:cam :y])) (not= target-zoom (get-in entities [:cam :zoom]))) (not (get-in entities [:cam :paused?]))) (-> entities (assoc-in [:cam :ideal-x] x) (assoc-in [:cam :ideal-y] y) (assoc-in [:tweens :cam-zoom] (tween/tween :cam-zoom screen [:cam :zoom] (get-in entities [:cam :zoom] 1.0) target-zoom duration :ease ease)) (assoc-in [:tweens :cam-x] (tween/tween :cam-x screen [:cam :x] (get-in entities [:cam :x] 160.0) target-x duration :ease ease)) (assoc-in [:tweens :cam-y] (tween/tween :cam-y screen [:cam :y] (get-in entities [:cam :y] 120.0) target-y duration :ease ease))) entities))) (defn jump-to [screen entities entity [x y] update-baseline?] (let [scale-fn (-> entities :room :scale-fn) entity (assoc entity :x x :y y) entity (if update-baseline? (assoc entity :baseline (- 240 y)) entity)] (if (:scaled entity) (assoc entity :scale-x (scale-fn [x y]) :scale-y (scale-fn [x y])) entity))) (defn find-animation [entity anim] (if (instance? Animation anim) anim (or (get-in entity [(:facing entity) anim]) (get entity anim)))) (defn start-animation ([entity anim] (start-animation {:total-time 0} entity anim)) ([screen entity anim] (let [new-anim (find-animation entity anim)] (if (and anim (not= new-anim (:anim entity))) (assoc entity :force-rerender true :anim new-anim :anim-start (:total-time screen)) entity)))) (defn stop [screen entities target-id & {:keys [face]}] (update-in entities [:room :entities target-id] (comp #(start-animation screen % (or (:stand-override %) :stand) ) (if face #(assoc % :facing face) identity)))) (defn do-stop [entities target-id & {:keys [face]}] (run-action entities (begin [this screen entities] (update-in entities [:room :entities target-id] (comp #(start-animation screen % (or (:stand-override %) :stand)) (if face #(assoc % :facing face) identity)))) (continue [this screen entities] entities) (done? [this screen entities] true) (terminate [this screen entities] entities) (skip-type [this screen entities] :skip))) (defn do-force-end [entities key] (run-action entities (begin [this screen entities] (if (get-in entities [key :script-running?]) (force-end entities (get-in entities [key :current]) key) entities)) (continue [this screen entities] entities) (done? [this screen entities] true) (terminate [this screen entities] entities) (skip-type [this screen entities] :none))) (defn walk-straight-to [entities target-id [final-x final-y] & {:keys [update-baseline? face speed anim override-dir stop?]}] (let [{start-x :x start-y :y} (get-in @entities [:room :entities target-id]) final-x (int final-x) final-y (int final-y) update-baseline? (if (nil? update-baseline?) true update-baseline?)] (run-action entities (begin [this screen entities] (if (= :ego target-id) (pan-to screen entities final-x final-y (get-in entities [:room :scale-fn])) entities)) (continue [this screen entities] (let [{from-x :x from-y :y :keys [left right scale-x] :as target-entity} (get-in entities [:room :entities target-id])] (let [delta-x (- final-x from-x) delta-y (- final-y from-y) distance (utils/dist from-x from-y final-x final-y) speed (* (or scale-x 1.0) (or speed 1.5)) speed (* speed (/ (:delta-time screen) (/ 1.0 60.0))) moved-x (if (= 0.0 distance) 0 (* speed (/ delta-x distance))) moved-y (if (= 0.0 distance) 0 (* speed (/ delta-y distance)))] (if (< distance speed) (update-in entities [:room :entities target-id] #(jump-to screen entities % [final-x final-y] update-baseline?)) (update-in entities [:room :entities target-id] #(start-animation screen (assoc (jump-to screen entities % [(+ moved-x from-x) (+ moved-y from-y)] update-baseline?) :facing (or override-dir (cond (< delta-x 0) :left (> delta-x 0) :right :else (:facing %)))) (or anim :walk) )))))) (done? [this screen entities] (let [{from-x :x from-y :y :keys [left right anim] :as target-entity} (get-in entities [:room :entities target-id])] (< (utils/dist final-x final-y from-x from-y) 1))) (terminate [this screen entities] (if (or (nil? stop?) stop?) (stop screen entities target-id :face face) entities)) (skip-type [this screen entities] :none)))) (defn play-animation [entities target-id anim & {:keys [stop? continue? next]}] (run-action entities (begin [this screen entities] (update-in entities [:room :entities target-id] #(start-animation screen % anim) )) (continue [this screen entities] entities) (done? [this screen entities] (animation! (find-animation (get-in entities [:room :entities target-id ]) anim) :is-animation-finished (- (:total-time screen) (get-in entities [:room :entities target-id :anim-start])))) (terminate [this screen entities] (if continue? entities (if (or (nil? stop?) stop?) (stop screen entities target-id) (assoc-in entities [:room :entities target-id :anim] next)))) (skip-type [this screen entities] :none))) (defn update-entity [entities target-id f] (run-action entities (begin [this screen entities] (update-in entities [:room :entities target-id] f)) (continue [this screen entities] entities) (done? [this screen entities] true) (terminate [this screen entities] entities) (skip-type [this screen entities] :none))) (defn stop-walking [entities target-id & {:keys [face]}] (run-action entities (begin [this screen entities] (stop screen entities target-id :face face)) (continue [this screen entities] entities) (done? [this screen entities] true) (terminate [this screen entities] entities) (skip-type [this screen entities] :none))) (defn walk-to [entities target-id [final-x final-y] & {:keys [skip-type face force-dir stop?] :or {stop? true}}] (let [{start-x :x start-y :y} (get-in @entities [:room :entities target-id]) final-x (int final-x) final-y (int final-y) path (vec (take-nth 4 (advent.pathfind/visit-all (:collision (:room @entities)) [(int start-x) (int start-y)] [final-x final-y]))) path (if (seq path) (conj path [final-x final-y]) []) targets-left (atom path)] (if (seq path) (run-action entities (begin [this screen entities] (if (= :ego target-id) (pan-to screen entities final-x final-y (get-in entities [:room :scale-fn])) entities)) (continue [this screen entities] (when (= (rand-int 5) 2) #_(particle-effect! (:step-particles entities) :start)) (loop [entities entities base-speed (* 1.5 (/ (:delta-time screen) (/ 1.0 60.0)) 1.0)] (let [{from-x :x from-y :y :keys [left right scale-x] :as target-entity} (get-in entities [:room :entities target-id]) [[target-x target-y] remainder] @targets-left] (let [delta-x (- target-x from-x) delta-y (- target-y from-y) distance (utils/dist from-x from-y target-x target-y) speed (* (or scale-x 1.0) base-speed) moved-x (if (= 0.0 distance) 0 (* speed (/ delta-x distance))) moved-y (if (= 0.0 distance) 0 (* speed (/ delta-y distance)))] (cond (<= speed 0) entities (> distance speed) (update-in entities [:room :entities target-id] #(start-animation screen (assoc (jump-to screen entities % [(+ moved-x from-x) (+ moved-y from-y)] true) :facing (cond force-dir force-dir (< delta-x 0) :left (> delta-x 0) :right :else (:facing %))) :walk )) (seq remainder) (do (swap! targets-left rest) (recur (-> entities (update-in [:room :entities target-id] #(jump-to screen entities % [target-x target-y] true)) (assoc-in [:step-particles :x] target-x) (assoc-in [:step-particles :y] target-y)) (- base-speed distance))) :else (-> entities (update-in [:room :entities target-id] #(jump-to screen entities % [target-x target-y] true)) (assoc-in [:step-particles :x] target-x) (assoc-in [:step-particles :y] target-y))))))) (done? [this screen entities] (let [{from-x :x from-y :y :keys [left right anim] :as target-entity} (get-in entities [:room :entities target-id])] (< (utils/dist final-x final-y from-x from-y) 1))) (terminate [this screen entities] (if stop? (stop screen entities target-id :face face) entities)) (skip-type [this screen entities] (or skip-type :none))) (do-stop entities :ego :face face)))) (defn get-text-duration [text] (max 1.5 (* (count (s/split text #"[ \-]")) 0.5))) (defn talk [entities target-id text & {:keys [stop? animate? anim wait color] :or {wait true}}] (let [initial-time (atom nil) stop? (if (nil? stop?) true stop?) animate? (if (nil? animate?) true animate?) fg-or-bg *fg-bg-key*] (run-action entities (begin [this screen entities] (let [_ (swap! initial-time #(or % (:total-time screen))) target-y (get-in entities [:room :entities target-id :y]) target-x (get-in entities [:room :entities target-id :x]) width (or (get-in entities [:room :entities target-id :width]) (texture! (get-in entities [:room :entities target-id]) :get-region-width)) origin-x (get-in entities [:room :entities target-id :origin-x]) target-x (if (nil? origin-x) (+ target-x (/ width 2)) target-x ) height (or (get-in entities [:room :entities target-id :height]) (texture! (get-in entities [:room :entities target-id]) :get-region-height)) scaled (get-in entities [:room :entities target-id :scaled]) scale-fn (get-in entities [:room :scale-fn]) scale (get-in entities [:room :entities target-id :scale-y] 1) height (* scale height)] (screen! dialogue/talking-screen :on-talk { :text text :id fg-or-bg :scene-viewport (:viewport screen) :x target-x :y (+ target-y height) :color (or color (get-in entities [:room :entities target-id :talk-color])) :target-id target-id :scale scale}) (if animate? (update-in entities [:room :entities target-id ] #(start-animation screen % (or anim (:talk-override %) :talk))) entities))) (continue [this screen entities] entities) (done? [this screen entities] (if wait (> (- (:total-time screen) @initial-time) (get-text-duration text)) true)) (terminate [this screen entities] (if wait (do (screen! dialogue/talking-screen :stop-talk {:id fg-or-bg}) (if stop? (stop screen entities target-id) entities)) entities)) (skip-type [this screen entities] :skip)))) (defn something-else [zipper] (-> zipper zip/up zip/up)) (defn previous-choices [zipper] (-> zipper zip/up)) (defn nth-child [zipper i] (loop [so-far 0 zipper (zip/down zipper)] (if (= so-far i) zipper (recur (inc so-far) (zip/right zipper))))) (defn make-zipper [tree] (zip/zipper map? #(apply concat (filter first (partition 2 (:choices %)))) (fn [n c] (assoc n :choices (vec c))) tree)) (defn present-choices [entities choices] (loop [zipper (make-zipper choices)] (let [selected-index (atom nil) node (zip/node zipper) dialogue-choices (filter first (partition 2 (:choices node)))] (run-action entities (begin [this screen entities] (screen! dialogue/choice-screen :on-present-choices { :choices dialogue-choices :callback #(reset! selected-index %)}) (assoc-in entities [:state :active?] false)) (continue [this screen entities] entities) (done? [this screen entities] (not (nil? @selected-index))) (terminate [this screen entities] (assoc-in entities [:state :active?] true)) (skip-type [this screen entities] :none)) (let [zipper (nth-child zipper (inc (* 2 @selected-index))) node (zip/node zipper)] (when-let [run (:run node)] (run (-> dialogue-choices (nth @selected-index) first))) (when-let [next-choices (or (when-let [choice-fn (:choice-fn node)] (choice-fn)) (:choices node))] (if (fn? next-choices) (recur (next-choices zipper)) (recur (zip/edit zipper assoc :choices next-choices)))))))) (defn update-entities [entities f & {:keys [use-screen?] :or {use-screen? false}}] (run-action entities (begin [this screen entities] (if use-screen? (f screen entities) (f entities))) (continue [this screen entities] entities) (done? [this screen entities] true) (terminate [this screen entities] entities) (skip-type [this screen entities] :none))) (defn pause-camera [entities] (update-entities entities #(-> % (assoc-in [:cam :paused? ] true) (update-in [:tweens] dissoc :cam-x :cam-y)))) (defn resume-camera [entities] (update-entities entities #(assoc-in % [:cam :paused? ] false))) (defn begin-animation [entities target-id anim] (run-action entities (begin [this screen entities] (update-in entities [:room :entities target-id] #(start-animation screen % anim))) (continue [this screen entities] entities) (done? [this screen entities] true) (terminate [this screen entities] entities) (skip-type [this screen entities] :none))) (defn update-state [entities f] (update-entities entities #(update-in % [:state] f))) (defn remove-item [entities item & {:keys [quiet?] :or {quiet? false}}] (run-action entities (begin [this screen entities] (when-not quiet? (screen! @(resolve 'advent.screens.scene/hud) :on-remove-item { :item ((:all-items entities) item)})) (-> entities (update-in [:state :inventory] #(remove (partial = item) %)))) (continue [this screen entities] entities) (done? [this screen entities] true) (terminate [this screen entities] entities) (skip-type [this screen entities] :none))) (defn play-sound ([entities sound-file vol & [wait?]] (let [wait? (if (nil? wait?) true wait?) s (if (string? sound-file) (utils/load-sound sound-file) sound-file)] (run-action entities (begin [this screen entities] (utils/play-sound! screen entities s (constantly vol))) (continue [this screen entities] entities) (done? [this screen entities] (if wait? (not (seq (filter #(= s (:sound %)) (get-in entities [:current-sounds :value])))) true)) (terminate [this screen entities] entities) (skip-type [this screen entities] :none))))) (defn give [entities item] (run-action entities (begin [this screen entities] (utils/play-sound! screen entities :pickup (constantly 0.3)) (screen! @(resolve 'advent.screens.scene/hud) :on-give-item { :item ((:all-items entities) item)}) (-> entities (update-in [:state :inventory] #(conj % item)) (update-in [:state :obtained-items] #(conj % item)))) (continue [this screen entities] entities) (done? [this screen entities] true) (terminate [this screen entities] entities) (skip-type [this screen entities] :none))) (defn remove-entity [entities entity] (run-action entities (begin [this screen entities] (update-in entities [:room :entities] #(dissoc % entity))) (continue [this screen entities] entities) (done? [this screen entities] true) (terminate [this screen entities] entities) (skip-type [this screen entities] :none ))) (defn add-entity [entities id entity] (run-action entities (begin [this screen entities] (update-in entities [:room :entities] #(assoc % id entity))) (continue [this screen entities] entities) (done? [this screen entities] true) (terminate [this screen entities] entities) (skip-type [this screen entities] :none))) (defn get-music [music time] (if (keyword? music) music (time music))) (defn transition-music [entities new-music & {:keys [duration between]}] (let [current-volume (atom 1.0) duration (or duration 2.0)] (run-action entities (begin [this screen entities] (assoc-in entities [:tweens :fade-out-music] (tween/tween :fade-out-music screen [:volume :value] 1.0 0.0 duration))) (continue [this screen entities] entities) (done? [this screen entities] (nil? (get-in entities [:tweens :fade-out-music]))) (terminate [this screen entities] (doseq [m (-> entities :musics vals) :when m] (music! m :stop)) (let [entities (-> entities ((or between identity)) (assoc-in [:music-override :value] new-music) (assoc-in [:volume :value] 1.0))] (music! (utils/get-current-music entities) :set-volume (utils/current-music-volume)) (music! (utils/get-current-music entities) :play) entities)) (skip-type [this screen entities] :none)))) (defn fade [entities time between] (run-action entities (begin [this screen entities] (-> entities (assoc-in [:tweens :fade-out] (tween/tween :fade-out screen [:fade :opacity] 0.0 1.0 time)) (assoc-in [:tweens :fade-out-music] (tween/tween :fade-out-music screen [:volume :value] 1.0 0.0 time)))) (continue [this screen entities] entities) (done? [this screen entities] (>= (get-in entities [:fade :opacity]) 1.0)) (terminate [this screen entities] (between entities)) (skip-type [this screen entities] :none)) (run-action entities (begin [this screen entities] (assoc-in entities [:tweens :fade-out] (tween/tween :fade-out screen [:fade :opacity] 1.0 0.0 time))) (continue [this screen entities] entities) (done? [this screen entities] (>= (get-in entities [:fade :opacity]) 0.0)) (terminate [this screen entities] (between entities)) (skip-type [this screen entities] :none))) (defn transition-background [entities new-background [x y] & {:keys [transition-music? between time face type]}] (let [transition-music? (if (nil? transition-music?) true transition-music?) type (or type :fade) old-music (get-music (get-in @entities [:room-musics (get-in @entities [:state :last-room])]) (get-in @entities [:state :time])) ;; TODO FIX new-music (get-music (get-in @entities [:room-musics new-background]) (get-in @entities [:state :time])) _ (println old-music new-music) music-changed? (and transition-music? (not= old-music new-music)) time (/ (float (or time 1.0)) 2.0)] (run-action entities (begin [this screen entities] (doseq [[k] (get-in entities [:room :timers])] (remove-timer! screen k)) (as-> entities e (assoc-in e [:tweens :fade-out] (tween/tween :fade-out screen [type :opacity] 0.0 1.0 time)) (if music-changed? (assoc-in e [:tweens :fade-out-music] (tween/tween :fade-out-music screen [:volume :value] (get-in entities [:volume :value]) 0.0 time)) e))) (continue [this screen entities] entities) (done? [this screen entities] (>= (get-in entities [type :opacity]) 1.0)) (terminate [this screen entities] (utils/release-resources screen :room) (let [entities (stop screen (if-let [next-time (get-in entities [:state :next-time])] (-> entities (assoc-in [:state :time] next-time) (assoc-in [:state :next-time] nil)) entities) :ego :face face) entities (if (get-in entities [:bg-actions :script-running?]) (force-end entities (get-in entities [:bg-actions :current]) :bg-actions) entities)] (if-let [stop-fn (get-in entities [:room :stop-fn])] (stop-fn screen entities) entities))) (skip-type [this screen entities] :none)) (log/info "Entering room " new-background ) (screen! dialogue/talking-screen :stop-talk { :id :bg-actions}) (run-action entities (begin [this screen entities] (utils/stop-all-sounds! entities) (let [ego (get-in entities [:room :entities :ego]) entities (as-> entities e (assoc-in e [:room] ((get-in entities [:rooms new-background]) screen)) (assoc-in e [:room :entities :ego] ego) (assoc-in e [:current-sounds :value] []) (if between (between screen e) e) (assoc-in e [:state :last-room] new-background) (assoc-in e [:tweens :fade-in] (tween/tween :fade-in screen [type :opacity] 1.0 0.0 time)) (if-not (get-in entities [:cam :paused?]) (-> e (update-in [:tweens] dissoc :cam-zoom :cam-x :cam-y) (assoc-in [:cam :ideal-x] x) (assoc-in [:cam :ideal-y] y) (assoc-in [:cam :x] (utils/bound-to-camera x 320 (get-in entities [:cam :zoom]))) (assoc-in [:cam :y] (utils/bound-to-camera y 240 (get-in entities [:cam :zoom])))) e) (if music-changed? (assoc-in e [:tweens :fade-in-music] (tween/tween :fade-in-music screen [:volume :value] 0.0 1.0 time)) e)) apply-state (get-in entities [:room :apply-state]) entities (if apply-state (apply-state screen entities) entities) #_#_entities (utils/update-override screen entities)] (when (and (not= new-music old-music) transition-music?) (doseq [[k v] (:musics entities) :when (and v (not= new-music k))] (music! v :stop)) (when (and new-music transition-music?) (music! (get-in entities [:musics new-music]) :set-volume 0) (music! (get-in entities [:musics new-music]) :play))) (-> entities (update-in [:room :entities :ego] #(jump-to screen entities % [x y] true))))) (continue [this screen entities] entities) (done? [this screen entities] (<= (get-in entities [type :opacity]) 0.0)) (terminate [this screen entities] (doseq [[k [start time fn]] (get-in entities [:room :timers])] (add-timer! screen k start time)) entities) (skip-type [this screen entities] :none)))) (defn do-dialogue [entities & pairs] (loop [pairs (partition 2 pairs)] (let [[[target line]] pairs next-speaker-is-different (not= target (ffirst (next pairs))) result (talk entities target line :stop? next-speaker-is-different)] (Thread/sleep 200) (if (seq (rest pairs)) (recur (rest pairs)) result)))) (defn respond [entities line & more] (apply do-dialogue entities :ego line more)) (defn tween [entities id tween-maker] (run-action entities (begin [this screen entities] (assoc-in entities [:tweens id] (tween-maker screen entities))) (continue [this screen entities] entities) (done? [this screen entities] (nil? (get-in entities [:tweens id]))) (terminate [this screen entities] entities) (skip-type [this screen entities] :none))) (defn glad [entities] (actions/run-action entities (begin [this screen entities] (println "ORIGIN" (get-in entities [:room :entities :ego :origin-x])) (let [current-y (get-in entities [:room :entities :ego :y]) to-y (+ current-y 15)] (-> entities (update-in [:room :entities :ego] #(actions/start-animation screen % :glad) ) (assoc-in [:tweens :jump] (tween/tween :jump screen [:room :entities :ego :y] current-y to-y 0.3 :ease tween/ease-out-cubic)) (assoc-in [:room :entities :glad-jump] (doto (assoc (particle-effect "particles/glad-jump") :x (get-in entities [:room :entities :ego :x]) :y (get-in entities [:room :entities :ego :y]) :baseline (get-in entities [:room :entities :ego :baseline])) (particle-effect! :reset) (particle-effect! :start))) ))) (continue [this screen entities] (assoc-in entities [:room :entities :glad-jump :y] (+ (get-in entities [:room :entities :ego :y]) 5)) ) (done? [this screen entities] (nil? (get-in entities [:tweens :jump]))) (terminate [this screen entities] entities) (skip-type [this screen entities] :none)) (Thread/sleep 800) (actions/run-action entities (begin [this screen entities] (let [current-y (get-in entities [:room :entities :ego :y]) to-y (- current-y 15)] (-> entities (assoc-in [:tweens :jump] (tween/tween :jump screen [:room :entities :ego :y] current-y to-y 0.1 :ease tween/ease-in-cubic))))) (continue [this screen entities] entities) (done? [this screen entities] (and (nil? (get-in entities [:tweens :jump])) (animation! (actions/find-animation (get-in entities [:room :entities :ego ]) :glad) :is-animation-finished (- (:total-time screen) (get-in entities [:room :entities :ego :anim-start]))))) (terminate [this screen entities] (stop screen entities :ego)) (skip-type [this screen entities] :none))) (defn fade-in-georgia [entities] (actions/run-action entities (begin [this screen entities] (particle-effect! (get-in entities [:room :entities :georgia-cloud]) :reset) (particle-effect! (get-in entities [:room :entities :georgia-cloud]) :start) (-> entities (assoc-in [:room :entities :georgia-face :x] (- (get-in entities [:room :entities :ego :x]) 30)) (assoc-in [:room :entities :georgia-face :y] (+ (get-in entities [:room :entities :ego :y]) 30)) (assoc-in [:room :entities :georgia-cloud :x] (- (get-in entities [:room :entities :ego :x]) 30)) (assoc-in [:room :entities :georgia-cloud :y] (+ (get-in entities [:room :entities :ego :y]) 30)) (assoc-in [:tweens :fade-georgia] (tween/tween :fade-georgia screen [:room :entities :georgia-face :opacity] 0.0 1.0 1.0 :ease tween/ease-in-cubic)))) (continue [this screen entities] (assoc-in entities [:room :entities :georgia-cloud :opacity] (get-in entities [:room :entities :georgia-face :opacity]))) (done? [this screen entities] (nil? (get-in entities [:tweens :fade-georgia]))) (terminate [this screen entities] entities) (skip-type [this screen entities] :none))) (defn fade-out-georgia [entities] (actions/run-action entities (begin [this screen entities] (particle-effect! (get-in entities [:room :entities :georgia-cloud]) :allow-completion) (-> entities (assoc-in [:tweens :fade-georgia] (tween/tween :fade-georgia screen [:room :entities :georgia-face :opacity] 1.0 0.0 1.0 :ease tween/ease-in-cubic)))) (continue [this screen entities] entities) (done? [this screen entities] (nil? (get-in entities [:tweens :fade-georgia]))) (terminate [this screen entities] entities) (skip-type [this screen entities] :none))) (defn georgia-say [entities msg] (transition-music entities :love :duration 0.5) (Thread/sleep 1000) (fade-in-georgia entities) (actions/talk entities :georgia-face msg) (Thread/sleep 1000) (fade-out-georgia entities) (transition-music entities nil :duration 1.0)) (defn wait-for-animation [entities target anim] (actions/run-action entities (begin [this screen entities] entities) (continue [this screen entities] entities) (done? [this screen entities] (animation! (actions/find-animation (get-in entities [:room :entities target]) anim) :is-animation-finished (- (:total-time screen) (get-in entities [:room :entities target :anim-start])))) (terminate [this screen entities] entities) (skip-type [this screen entities] :none))) (defn in-love [entities] (let [seen-love (get-in @entities [:state :has-seen-love?])] (when (or (not seen-love) (< (rand-int 10) 3)) (transition-music entities :love :duration 1.0) (begin-animation entities :georgia-face :love) (fade-in-georgia entities) (update-state entities (fn [s] (assoc s :has-seen-love? true))) (wait-for-animation entities :georgia-face :love) (fade-out-georgia entities) (do-stop entities :georgia-face) (transition-music entities nil :duration 1.0)))) (defn do-pan [entities x y scale-fn & [ease duration]] (actions/run-action entities (begin [this screen entities] (pan-to screen entities x y scale-fn ease duration)) (continue [this screen entities] entities) (done? [this screen entities] (nil? (get-in entities [:tweens :cam-y]))) (terminate [this screen entities] entities) (skip-type [this screen entities] :none))) (defn camera-shake [entities length] (dotimes [n length] (actions/do-pan entities (get-in @entities [:cam :x]) (+ (get-in @entities [:cam :y]) 3) (constantly (get-in @entities [:cam :zoom])) nil (* 0.01 (inc n))) (actions/do-pan entities (get-in @entities [:cam :x]) (- (get-in @entities [:cam :y]) 3) (constantly (get-in @entities [:cam :zoom])) nil (* 0.01 (inc n))))) (defn squat-talk [entities & msgs] (when (seq msgs) (do (actions/talk entities :ego (first msgs) :anim :squat-talk :stop? false) (Thread/sleep 200) (recur entities (rest msgs))))) (defn wait [entities time] (let [initial-time (atom nil)] (run-action entities (begin [this screen entities] (reset! initial-time (:total-time screen)) entities) (continue [this screen entities] entities) (done? [this screen entities] (> (* 1000 (- (:total-time screen) @initial-time)) time)) (terminate [this screen entities] entities) (skip-type [this screen entities] :none)))) (defn play-safe ([entities] (play-safe entities true)) ([entities whistle?] (let [safe-song-anim (utils/make-anim-seq (texture-atlas "packed/global.atlas") "safe-song/safe-song" [100 35] 0.05 (flatten [ [1 2 3] (repeat 2 [ [5 6 7 8 8 7 6 6 6 6 6] (map (partial + 4) [5 6 7 8 8 7 6 6 6 6 6]) (map (partial + 8) [5 6 7 8 8 7 6 6 6 6 6]) (map (partial + 12) [5 6 7 8 8 7 6 6 6 6 6]) 18 18 18 18 (map (partial + 16) [5 6 7 8 8 7 6 6 6 6 6]) (repeat 45 22)]) [22 22 22 22 22 22 22 22 26 27 28 28 28 28 28 28 28 28 28 28 28 28 28 28] ]))] (when whistle? (wait entities 200) (begin-animation entities :ego :whistle)) (wait entities 400) (add-entity entities :safe-song (rooms/make-entity :safe-song (assoc (utils/atlas->texture (texture-atlas "packed/global.atlas") "safe-song/safe-song" 0) :x 110 :y 120 :baseline 241 :night-profile :none :anim nil :anim-start 0 :play safe-song-anim))) (begin-animation entities :safe-song :play) (wait entities 100) (play-sound entities "safe-sound.ogg" 0.1 false) (wait entities 10800) (remove-entity entities :safe-song) (when whistle? (do-stop entities :ego)))))