Files
gitea-docker/desktop/src-common/advent/actions.clj

1028 lines
50 KiB
Clojure

(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]
[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# (<!! c#)]
(if (= :end result#)
(do
(log/info "ending script")
(throw (ex-info "User quit script" {:type :end-script})))
(reset! ~entities result#))))))
(defn change-script-state [entities state]
(let [fg-bg-key *fg-bg-key*]
(run-action entities
(begin [this screen entities]
(-> 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 (binding [*fg-bg-key* :fg-actions]
~@forms
(change-script-state ~entities false)
(utils/save @~entities "autosave" "Autosave"))))))))
(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
: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)
(-> entities
(assoc-in [:room :entities target-id :x] final-x)
(assoc-in [:room :entities target-id :y] final-y))
(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))
(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) 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)
(do (swap! targets-left rest)
(-> entities
(assoc-in [:room :entities target-id :x] target-x)
(assoc-in [:room :entities target-id :y] target-y)
(assoc-in [:step-particles :x] target-x)
(assoc-in [:step-particles :y] target-y)))
(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
))))))
(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)
(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 transition-background [entities new-background [x y] & {:keys [transition-music? between time face]}]
(let [transition-music? (if (nil? transition-music?) true transition-music?)
old-music (get-music (get-in @entities [:room :music]) (get-in @entities [:state :time]))
new-music (get-music (get-in @entities [:rooms new-background :music]) (get-in @entities [:state :time]))
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 [:fade :opacity] 0.0 1.0 time))
(if music-changed?
(assoc-in e [:tweens :fade-out-music] (tween/tween :fade-out-music screen [:volume :value] 1.0 0.0 time))
e)))
(continue [this screen entities]
entities)
(done? [this screen entities]
(>= (get-in entities [:fade :opacity]) 1.0))
(terminate [this screen entities]
(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])
old-music (get-music (get-in entities [:room :music]) (get-in entities [:state :time]))
entities (as-> entities e
(assoc-in e [:room] (get-in entities [:rooms new-background]))
(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 [:fade :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))
new-music (get-music (get-in entities [:room :music]) (get-in entities [:state :time]))
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 [:fade :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 glad [entities]
(actions/run-action entities
(begin [this screen entities]
(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)))
(assoc-in [:room :entities :cloud] (assoc (utils/get-texture "space/cloud.png")
:x (get-in entities [:room :entities :ego :x])
:y (get-in entities [:room :entities :ego :y])
:origin-x 7
:origin-y 7
:scale-x 0.5
:scale-y 0.5
:opacity 0.5
:baseline 240))
(assoc-in [:tweens :cloud-up] (tween/tween :cloud-up screen [:room :entities :cloud :y]
(get-in entities [:room :entities :ego :y])
(+ (get-in entities [:room :entities :ego :y]) 10)
1.0))
(assoc-in [:tweens :cloud-fade] (tween/tween :cloud-fade screen [:room :entities :cloud :opacity]
0.5
0.0
1.0))
(assoc-in [:tweens :cloud-grow] (tween/tween :cloud-grow screen [:room :entities :cloud :scale-y]
0.5
1.0
1.0))
(assoc-in [:tweens :cloud-grow-2] (tween/tween :cloud-grow-2 screen [:room :entities :cloud :scale-x]
0.5
1.0
1.0)))))
(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 "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/get-texture "safe-song/safe-song-1")
: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)))))