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

200 lines
8.6 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]
[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]))
(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 (<!! c#)))))
(defn walk-to [entities target-id [final-x final-y]]
(let [{start-x :x start-y :y} (get-in @entities [:background :entities target-id])
final-x (int final-x)
final-y (int final-y)
path (vec (take-nth 5 (advent.pathfind/visit-all
(:collision (:background @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]
entities)
(continue [this screen entities]
(let [{from-x :x from-y :y :keys [left right] :as target-entity} (get-in entities [:background :entities target-id])
[[target-x target-y] remainder] @targets-left]
(let [delta-x (- target-x from-x)
delta-y (- target-y from-y)
distance (dist from-x from-y target-x target-y)
moved-x (if (= 0.0 distance)
0
(* 1.5 (/ delta-x distance)))
moved-y (if (= 0.0 distance)
0
(* 1.5 (/ delta-y distance)))]
(if (< distance 1)
(do (swap! targets-left rest)
entities)
(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 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))))