375 lines
14 KiB
Clojure
375 lines
14 KiB
Clojure
(ns advent.utils
|
|
(:refer-clojure :exclude [load])
|
|
(:require [play-clj.core :refer :all]
|
|
[play-clj.ui :refer :all]
|
|
[play-clj.utils :refer :all]
|
|
[play-clj.math :refer :all]
|
|
[play-clj.g2d :refer :all]
|
|
[clojure.java.io :as io]
|
|
[clojure.edn :as edn]
|
|
[clojure.string :as str])
|
|
(:import [com.badlogic.gdx.graphics Pixmap Pixmap$Blending Pixmap$Filter Texture Texture$TextureFilter]
|
|
[com.badlogic.gdx.graphics.g2d TextureRegion Animation]
|
|
[com.badlogic.gdx.utils.viewport FitViewport]
|
|
[com.badlogic.gdx.scenes.scene2d Actor Stage]
|
|
[com.badlogic.gdx.math CatmullRomSpline]
|
|
[com.badlogic.gdx Application Audio Files Game Gdx Graphics Input
|
|
InputMultiplexer InputProcessor Net Preferences Screen]
|
|
[java.lang Object]))
|
|
|
|
(defn log-coords [screen entities]
|
|
(let [{:keys [x y]} (input->screen screen {:x (:input-x screen) :y (:input-y screen)})]
|
|
(println (:input-x screen) (:input-y screen) "->" x y)))
|
|
|
|
(def +all-cursors+ [:main :wool :mushrooms :carrot :right :down :left :up :flask :flask-with-contents :trophy :ladder :stick :cat-toy :balloon :frog-legs :teddy :portrait :recipe :glass-eye :motivational-tapes :used-earplugs :grass :slobber :flask-with-strength :medal :kiss :sword :hourglass :mandrake :ball-n-chain :key :rope :crowbar :note-1 :ash :sack-lunch :flies :spear :monocle :feather :spell-component :money :watch :broken-clock :slingshot :camera :walkie-talkies :alarm-clock :walkie-talkie :flask-water :flask-water-stuff :flask-water-stuff-2 :note-2 :magic-slingshot :active-main :shovel :broom])
|
|
|
|
(def settings (atom {:music-volume 50.0
|
|
:sound-volume 75.0}))
|
|
|
|
(defn current-music-volume [& [factor]]
|
|
(* (Math/pow (/ (:music-volume @settings) 100.0) 2)
|
|
0.25
|
|
(or factor 1.0)))
|
|
|
|
(defn current-sound-volume [& [factor]]
|
|
(* (Math/pow (/ (:sound-volume @settings) 100.0) 2)
|
|
0.5
|
|
(or factor 1.0)))
|
|
|
|
(defn cursor [filename which]
|
|
(let [scale 2
|
|
base-cursor (pixmap filename)
|
|
target-width (* 16 scale)
|
|
target-height (* 16 scale)
|
|
resized (Pixmap. target-width target-height (.getFormat base-cursor))
|
|
index (.indexOf +all-cursors+ which)]
|
|
#_(Pixmap/setFilter Pixmap$Filter/NearestNeighbour)
|
|
(pixmap! resized :draw-pixmap base-cursor (* index 16) 0 16 16
|
|
0 0 target-width target-height)
|
|
|
|
resized ))
|
|
|
|
(defn get-texture [path]
|
|
(let [atlas-name (str/replace path #".png" "")
|
|
atlas (texture-atlas "packed/pack.atlas")]
|
|
(texture (texture-atlas! atlas :find-region atlas-name))))
|
|
|
|
(defn save [entities]
|
|
(doto (.getPreferences (Gdx/app) "ticks-tales-saves")
|
|
(.putString "save-1" (pr-str (entities :state)))
|
|
.flush))
|
|
|
|
(defn has-save? []
|
|
(-> (.getPreferences (Gdx/app) "ticks-tales-saves")
|
|
(.contains "save-1")))
|
|
|
|
(defn load []
|
|
(-> (.getPreferences (Gdx/app) "ticks-tales-saves")
|
|
(.getString "save-1")
|
|
edn/read-string
|
|
(assoc :active? true)))
|
|
|
|
(defn save-settings [entities]
|
|
(doto (.getPreferences (Gdx/app) "ticks-tales-saves")
|
|
(.putString "settings" (pr-str @settings))
|
|
.flush))
|
|
|
|
(defn has-saved-settings? []
|
|
(-> (.getPreferences (Gdx/app) "ticks-tales-saves")
|
|
(.contains "settings")))
|
|
|
|
(defn load-settings []
|
|
(reset! settings
|
|
(-> (.getPreferences (Gdx/app) "ticks-tales-saves")
|
|
(.getString "settings")
|
|
edn/read-string)))
|
|
|
|
|
|
(defn load-settings! []
|
|
(when (.exists (io/file "settings.edn"))
|
|
(reset! settings (edn/read-string (slurp "settings.edn")))))
|
|
|
|
(defn save-settings! []
|
|
(spit "settings.edn" @settings))
|
|
|
|
(defn get-font [filename]
|
|
(let [font (bitmap-font filename)
|
|
tr (bitmap-font! font :get-region)
|
|
tx (.getTexture tr)]
|
|
(call! ^Texture tx :set-filter Texture$TextureFilter/Linear Texture$TextureFilter/Linear)
|
|
font))
|
|
|
|
(def +screen-width+ 320)
|
|
(def +screen-height+ 240)
|
|
|
|
(defn scaler-fn-with-baseline [baseline minimum-size & [maximum-size]]
|
|
(let [maximum-size (or maximum-size 1.0)]
|
|
(fn [[_ y]]
|
|
(if (< y baseline) maximum-size
|
|
(let [percent-complete (- 1.0 (/ (- y baseline) (- +screen-height+ baseline)))
|
|
range (+ (* percent-complete (- maximum-size minimum-size)) minimum-size)]
|
|
range)))))
|
|
|
|
(defn get-scale-value [image x y]
|
|
(let [base-y (Math/floor (- 240 y))
|
|
other-y (Math/ceil (- 240 y))
|
|
base-amount (- y (Math/floor y))
|
|
other-amount (- 1.0 base-amount)
|
|
base-v (-> image
|
|
(pixmap! :get-pixel x base-y)
|
|
color
|
|
(.r))
|
|
other-v (-> image
|
|
(pixmap! :get-pixel x other-y)
|
|
color
|
|
(.r))]
|
|
(+ (* base-v base-amount)
|
|
(* other-v other-amount))))
|
|
|
|
(defn scaler-fn-from-image [image minimum-size maximum-size]
|
|
(let [image (pixmap image)
|
|
maximum-size (or maximum-size 1.0)]
|
|
(fn [[x y]]
|
|
(let [percent-complete (get-scale-value image x y)]
|
|
(if (< y 0)
|
|
maximum-size
|
|
(+ (* percent-complete (- maximum-size minimum-size)) minimum-size))))))
|
|
|
|
(defn dist [x1 y1 x2 y2 & {:keys [y-sign x-sign]}]
|
|
(let [y-sign (or y-sign 1.0)
|
|
x-sign (or x-sign 1.0)
|
|
dx (* (- x1 x2) x-sign)
|
|
dy (* y-sign (- y1 y2))]
|
|
(Math/sqrt (+ (* dx dx) (* dy dy)))))
|
|
|
|
|
|
(defn flip [^Animation anim]
|
|
(animation (animation! anim :get-frame-duration)
|
|
(for [src-frame (animation! anim :get-key-frames)
|
|
:let [frame (texture (texture! src-frame :get-texture))]]
|
|
(do
|
|
(texture! frame :set-region ^TextureRegion src-frame)
|
|
(texture! frame :flip true false)
|
|
frame))))
|
|
|
|
(defn split-texture [file [w h] frames]
|
|
(let [sheet (texture! (get-texture file) :split w h)]
|
|
(for [i frames]
|
|
(aget sheet 0 i))))
|
|
|
|
(defn make-anim [file [w h] speed frames]
|
|
(animation speed (split-texture file [w h] frames)))
|
|
|
|
(defn make-anim-seq [file [w h] speed frames]
|
|
(animation speed (map #(get-texture (str file "-" (inc %) ".png")) frames)))
|
|
|
|
(defn make-bird [screen p]
|
|
(let [bird-sheet (texture! (get-texture "outside-castle/bird.png") :split 1 2)
|
|
bird-stand (animation 0.15 (for [i [0 1]]
|
|
(aget bird-sheet 0 i)))]
|
|
(assoc (animation->texture screen bird-stand)
|
|
:x 0
|
|
:y 0
|
|
:baseline 21
|
|
:anim bird-stand
|
|
:anim-start 0
|
|
:path (catmull-rom-spline (map #(apply vector-2* %) p) true)
|
|
:update-fn (fn [screen entities entity]
|
|
(let [speed 0.05
|
|
pos-f (- (* (:total-time screen) speed) (int (* (:total-time screen) speed)))
|
|
v (vector-2 0 0)
|
|
a (catmull-rom-spline! ^CatmullRomSpline (:path entity) :value-at v pos-f)]
|
|
(assoc entity :x (vector-2! v :x) :y (vector-2! v :y)))))))
|
|
|
|
(defn path-point [speed screen entities entity]
|
|
(* (- (:total-time screen) (:path-start-time entity 0.0)) speed)
|
|
)
|
|
|
|
(defn update-path-location [speed screen entities entity]
|
|
(if (:path entity)
|
|
(let [pos-f (- (path-point speed screen entities entity) (int (* (- (:total-time screen) (:path-start-time entity 0.0)) speed)))
|
|
v (vector-2 0 0)
|
|
a (catmull-rom-spline! ^CatmullRomSpline (:path entity) :value-at v pos-f)]
|
|
(assoc entity :x (vector-2! v :x) :y (vector-2! v :y)))
|
|
entity))
|
|
|
|
(defn find-override [entities [x y]]
|
|
(first (concat (filter #(and ((:mouse-in? %) entities x y)
|
|
|
|
(:override %))
|
|
(get-in entities [:room :interactions]))
|
|
(filter #(and (:mouse-in? %)
|
|
((:mouse-in? %) entities x y)
|
|
(not= "ego" (:id %))
|
|
(:script %))
|
|
(vals (get-in entities [:room :entities])))
|
|
(filter #(and ((:mouse-in? %) entities x y)
|
|
(:script %))
|
|
(get-in entities [:room :interactions])))))
|
|
|
|
(defn hovering-ego [entities [x y]]
|
|
(when-let [mouse-in (get-in entities [:room :entities :ego :mouse-in?])]
|
|
(mouse-in entities x y)))
|
|
|
|
(defn remove-interaction [entities id]
|
|
(update-in entities [:room :interactions] (fn [i] (remove #(= id (:id %)) i))))
|
|
|
|
|
|
(defn play-sound [snd]
|
|
(music! snd :play))
|
|
|
|
(defn stop-sound [snd]
|
|
(music! snd :stop))
|
|
|
|
(defn make-music [r]
|
|
(try
|
|
(doto (music r) (music! :set-looping true))
|
|
(catch Exception _
|
|
(doto (music (str r ".mp3")) (music! :set-looping true)))))
|
|
|
|
(defn load-sound [f]
|
|
(try
|
|
(sound (str f ".mp3"))
|
|
(catch Exception _
|
|
(sound (str f ".mp3")))))
|
|
|
|
|
|
|
|
(defn apply-tweens [screen entities tweens]
|
|
(reduce (fn [e f]
|
|
(f e (:total-time screen)))
|
|
entities
|
|
(vals tweens)))
|
|
|
|
(defn intersects? [e [x y]]
|
|
(and (:object e)
|
|
(< (:x e) x (+ (:x e) (or (:width e) (.getWidth ^Actor (:object e)))))
|
|
(< (:y e) y (+ (:y e) (or (:height e) (.getHeight ^Actor (:object e)))))))
|
|
|
|
(defn get-current-music [entities]
|
|
(let [time (get-in entities [:state :time])
|
|
musics (:musics entities)
|
|
override-music (musics (get-in entities [:music-override :value]))
|
|
current-music (musics (get-in entities [:room :music]))
|
|
current-time-music (musics (get-in entities [:room :music time]))]
|
|
(or override-music current-music current-time-music)))
|
|
|
|
|
|
(defn setup-viewport [screen width height]
|
|
(let [cam (or (:cam screen) (orthographic))
|
|
viewport (FitViewport. width height cam)
|
|
stage (Stage. viewport)]
|
|
(update! screen :renderer stage :viewport viewport :camera cam)
|
|
(.apply viewport)
|
|
[cam viewport stage]))
|
|
|
|
(defn unproject
|
|
([screen]
|
|
(unproject screen [(:input-x screen) (:input-y screen)]) )
|
|
([screen [x y]]
|
|
(let [pj (.unproject ^FitViewport (:viewport screen) (vector-2 x y))
|
|
x (.x pj)
|
|
y (.y pj)]
|
|
[x y])))
|
|
|
|
(defn project
|
|
([screen [x y]]
|
|
(let [pj (.project ^FitViewport (:viewport screen) (vector-2 x y))
|
|
x (.x pj)
|
|
y (.y pj)]
|
|
[x y])))
|
|
|
|
(defn contains-point? [x1 y1 width height x y]
|
|
(and (< x1 x (+ x1 width))
|
|
(< y1 y (+ y1 height))))
|
|
|
|
(defn is-unstoppable-script-running [screen entities]
|
|
(let [current-action (get-in entities [:actions :current])
|
|
is-script-running (get-in entities [:actions :script-running?])]
|
|
(and is-script-running
|
|
(= :none (get-in entities [:actions :last-skip-type])))))
|
|
|
|
(defn update-override [{:keys [^FitViewport viewport] :as screen} entities]
|
|
(let [raw-pos (get-in entities [:cursor :last-pos])
|
|
last-pos (unproject screen raw-pos)
|
|
mouse-override (find-override entities last-pos)
|
|
out-of-bounds (not (contains-point? (.getScreenX viewport) (.getScreenY viewport)
|
|
(.getScreenWidth viewport) (.getScreenHeight viewport)
|
|
(first raw-pos) (last raw-pos)))
|
|
hovering-ego (hovering-ego entities last-pos)
|
|
current (get-in entities [:cursor :current])]
|
|
|
|
(cond
|
|
out-of-bounds
|
|
(update-in entities [:cursor] assoc :override nil :active false )
|
|
|
|
|
|
(not (get-in entities [:state :active?]))
|
|
(-> entities
|
|
(assoc-in [:cursor :override] nil)
|
|
(assoc-in [:cursor :active] false))
|
|
|
|
(is-unstoppable-script-running screen entities)
|
|
(-> entities
|
|
(assoc-in [:cursor :override] :hourglass)
|
|
(assoc-in [:cursor :active] false))
|
|
|
|
(get-in entities [:state :hud-active?])
|
|
(-> entities
|
|
(assoc-in [:cursor :override] nil)
|
|
(assoc-in [:cursor :active] false))
|
|
|
|
(and hovering-ego (not= :main current))
|
|
(-> entities
|
|
(assoc-in [:cursor :override] (or (:cursor mouse-override) (when (#{:main :active-main} (get-in entities [:cursor :last])) :active-main)))
|
|
(assoc-in [:cursor :active] true))
|
|
|
|
mouse-override
|
|
(-> entities
|
|
(assoc-in [:cursor :override] (or (:cursor mouse-override) (when (#{:main :active-main} (get-in entities [:cursor :last])) :active-main)))
|
|
(assoc-in [:cursor :active] true))
|
|
|
|
|
|
|
|
:else
|
|
(-> entities
|
|
(assoc-in [:cursor :override] nil)
|
|
(assoc-in [:cursor :active] false)))))
|
|
|
|
(def default-night-merge {:r 0.08 :g 0.1 :b 0.36 :multiply-amount 1.0 :hue-amount 1.0})
|
|
(def default-night-merge-sprite {:r 0.08 :g 0.1 :b 0.36 :multiply-amount 0.3 :hue-amount 0.4})
|
|
|
|
(defn clamp-volume [vol]
|
|
(max vol 0.005))
|
|
|
|
(defn proximity-volume [entities [x y] & {:keys [scale]}]
|
|
(-> (max 0.0
|
|
(- 1.0 (/ (dist x y
|
|
(get-in entities [:room :entities :ego :x])
|
|
(get-in entities [:room :entities :ego :y])
|
|
:y-sign 2.0
|
|
:x-sign (/ 1.0 (get-in entities [:room :entities :ego :scale-x])))
|
|
175.0)))
|
|
(* (or scale 1.0))
|
|
(clamp-volume)))
|
|
|
|
(defn fast-forward-particle [p]
|
|
(particle-effect! p :reset)
|
|
(particle-effect! p :start)
|
|
|
|
(particle-effect! p :update 10.0)
|
|
(dotimes [_ 160]
|
|
(particle-effect! p :update 0.2)))
|
|
|
|
(defn toggle-fullscreen! []
|
|
(if (.isFullscreen Gdx/graphics)
|
|
(on-gl (.setDisplayMode Gdx/graphics 1280 960 false))
|
|
(on-gl (.setDisplayMode Gdx/graphics
|
|
(doto (.width (.getDesktopDisplayMode Gdx/graphics)) println)
|
|
(doto (.height (.getDesktopDisplayMode Gdx/graphics)) println)
|
|
true)
|
|
(set! (.foregroundFPS (-> (class Gdx/graphics)
|
|
(.getDeclaredField (name "config"))
|
|
(doto (.setAccessible true))
|
|
(.get Gdx/graphics))) 60))))
|