(ns advent.screens.title (:require [clojure.string :as str] [play-clj.core :refer :all] [play-clj.math :refer :all] [play-clj.ui :refer :all] [play-clj.utils :refer :all] [play-clj.g2d :refer :all] [advent.utils :as utils] [advent.saves :as saves] [advent.tween :as tween] [advent.steam :as steam] [advent.screens.scene :as scene] [advent.screens.credits :as credits] [advent.screens.dialogue :as dialogue] [clojure.tools.logging :as log] [advent.screens.title :as title] [advent.screens.inventory :as inventory] [advent.screens.safe :as safe] [advent.screens.fade :as fade] [advent.version] ) (:import [com.badlogic.gdx.graphics Pixmap Pixmap$Filter Texture Texture$TextureFilter Color] [com.badlogic.gdx.graphics.g2d TextureRegion NinePatch] [play_clj.entities NinePatchEntity] [com.badlogic.gdx.utils.viewport FitViewport] [com.badlogic.gdx.scenes.scene2d.ui Slider$SliderStyle Widget ButtonGroup TextButton$TextButtonStyle CheckBox$CheckBoxStyle CheckBox Button ImageButton ScrollPane] [com.badlogic.gdx.scenes.scene2d Group Actor Stage] [play_clj.entities ActorEntity] [com.badlogic.gdx.utils Align] [com.badlogic.gdx.scenes.scene2d.utils NinePatchDrawable TextureRegionDrawable BaseDrawable] [com.badlogic.gdx Application Audio Files Game Gdx Graphics Input InputMultiplexer InputProcessor Net Preferences Screen])) (defn get-color [e mouse-pos] (if (utils/intersects? e mouse-pos) (color :yellow) (color 0.242 0.105 0.355 1.0))) (def button-color (Color/valueOf "9b399eff")) (def label-color (color 0.81 0.5 0.0 1.0)) (defn style-label [e font mouse-pos] (when (:interactable e) (label! e :set-style (style :label font (get-color e mouse-pos)))) e) (defn sliding-window [i size xs] (if (<= (count xs) size) xs (let [drop-count (min (max 0 (- i (/ size 2))) (- (count xs) size))] (take size (drop drop-count xs))))) (defn style-slider [s mouse-pos] (if (utils/intersects? s mouse-pos) (slider! s :set-style ^Slider$SliderStyle (:hover s)) (slider! s :set-style ^Slider$SliderStyle (:default s))) s) (defn center [e] (assoc e :x (- (/ 1280 2) (/ (or (:width e) (.getWidth ^Actor (:object e))) 2)))) (defn get-dir [old-x new-x] (if (< old-x new-x) :right :left)) (def do-once (atom false)) (defn quit [screen entities] (do (log/info "Quitting.") (graphics! :set-cursor (utils/cursor "cursor.png" :hourglass)) (-> entities (assoc-in [:tweens :fade-out] (tween/tween :fade-out screen [:fade :opacity] 0.0 1.0 1.0 :finish (fn [entities] (System/exit 0) entities) :ease tween/ease-in-cubic))))) (defn fly-balloon [screen entities] (let [speed 0.03 pos-f (- (* (:total-time screen) speed) (int (* (:total-time screen) speed))) v (vector-2 0 0) a (catmull-rom-spline! (:path (:balloon entities)) :value-at v pos-f)] (-> entities (update-in [:balloon] merge {:x (vector-2! v :x) :y (vector-2! v :y)})))) (defn fly-ego [screen entities] (let [speed 0.07 pos-f (- (* (:total-time screen) speed) (int (* (:total-time screen) speed))) v (vector-2 0 0) a (catmull-rom-spline! (:path (:flying-ego entities)) :value-at v pos-f) direction (get-dir (get-in entities [:flying-ego :x]) (vector-2! v :x))] (-> entities (update-in [:flying-ego] merge {:x (vector-2! v :x) :y (vector-2! v :y)} (get-in entities [:flying-ego direction])) (update-in [:ego-jet] merge {:x (+ 40 (vector-2! v :x)) :y (vector-2! v :y)})))) (defn flip [t] (let [flipped (texture t)] (texture! flipped :flip true false) flipped)) (defn quest-label [] (if (seq (utils/snapshot-list)) "Continue Quest" "Begin Quest")) (defn start-playing [screen entities save] (do (log/info "Starting/Continuing quest.") (graphics! :set-cursor (utils/cursor "cursor.png" :hourglass)) (-> entities (assoc-in [:tweens :fade-out] (tween/tween :fade-out screen [:fade :opacity] 0.0 1.0 1.0 :finish (fn [entities] (utils/stop-music (:music entities)) (reset! utils/selected-save save) (set-screen! @(resolve 'advent.core/advent) scene/scene #_scene/demo scene/hud dialogue/tooltip-screen dialogue/talking-screen dialogue/choice-screen dialogue/toast-screen inventory/inventory-screen safe/safe-screen fade/fade-screen ) entities) :ease tween/ease-in-cubic)) (assoc-in [:tweens :fade-out-music] (tween/tween :fade-out-music screen [:volume] 1.0 0.0 1.0))))) (defn button-style [] (let [^TextButton$TextButtonStyle style (skin! (skin "ui/ui.json") :get TextButton$TextButtonStyle) tx (-> style .font .getRegion .getTexture)] (-> style .font .getData (.setScale 1.0)) (call! ^Texture tx :set-filter Texture$TextureFilter/Linear Texture$TextureFilter/Linear) style)) (defn save-object [a] (.setUserObject ^Actor (:object a) a) a) (defn make-button ([msg & rest] (let [button (merge (text-button msg (button-style)) (apply hash-map rest))] (doto button save-object )))) (defn set-checkbox-state [cb state] (-> Button (.getDeclaredField (name "isChecked")) (doto (.setAccessible true)) (.set (:object cb) state)) cb) (defn make-checkbox ([msg checked & rest] (let [s (skin! (skin "ui/ui.json") :get CheckBox$CheckBoxStyle)] (-> s .font .getData (.setScale 0.5)) (doto (merge (check-box msg s) (apply hash-map rest)) (set-checkbox-state checked) save-object)))) (defn make-label ([msg] (make-label msg nil)) ([msg col] (let [font (utils/get-font "ego/font.fnt") p (NinePatchEntity. (skin! (skin "ui/ui.json") :get-patch "ui-bg")) _ (nine-patch! p :set-padding 0 0 0 0) bg (drawable :nine-patch ^NinePatch (:object p)) style (style :label font (or col (color 1.0 0.3 0.3 1.0))) #_#__ (set! (.background style) bg)] (-> style .font .getData (.setScale 0.5)) (-> msg (label style) (assoc :x 0 :y 0 :height 40 :origin-x 0 :origin-y 0 :z 8) (doto (label! :set-alignment Align/bottom) #_(#(label! % :set-width (/ (label! % :get-width) 2)))) center)))) (defn make-slider [initial-value & rest] (let [ui-skin (skin "ui/ui.json") slider (-> (slider {:min 0 :max 100 :step 1} ui-skin :set-value initial-value) (assoc :width 305 :hover (skin! ui-skin :get "default-horizontal-hover" Slider$SliderStyle) :default (skin! ui-skin :get "default-horizontal" Slider$SliderStyle) :z 8) (merge (apply hash-map rest)))] (doto slider save-object))) (defn stack-y [label base index] (assoc label :y (- base (* 50 index)))) (defn make-table [children] (-> children (table) (doto (table! :set-background (NinePatchDrawable. (skin! (skin "ui/ui.json") :get-patch "ui-bg" )))) (assoc :y 32 :width (* utils/ui-scale 504) :height (* utils/ui-scale 335) :x (- (/ 1280 2) (/ (* utils/ui-scale 504) 2)) :z 8 :opacity 1.0))) (defn main-menu [screen] (let [start-playing-label (quest-label) is-starting? (= "Begin Quest" start-playing-label)] (make-table [[(make-button start-playing-label :key :continue-or-start) :height (* utils/ui-scale 56) :pad-bottom 4 :width (* utils/ui-scale 250)] :row [(doto (make-button "Load" :key :load) (text-button! :set-disabled (not (seq (utils/snapshot-list))))) :height (* utils/ui-scale 56) :pad-bottom 4 :width (* utils/ui-scale 250)] :row [(doto (make-button "Chapters" :key :chapters) (text-button! :set-disabled (not (->> (utils/get-chapters) (vals) (filter identity) seq)))) :height (* utils/ui-scale 56) :pad-bottom 4 :width (* utils/ui-scale 250)] :row [(make-button "Settings" :key :settings) :height (* utils/ui-scale 56) :pad-bottom 4 :width (* utils/ui-scale 250)] :row [(make-button "End Quest" :key :end-quest) :height (* utils/ui-scale 56) :width (* utils/ui-scale 250)] ]))) (defn chapters-menu [] (let [chapters (utils/get-chapters) chapter-table (table (mapcat identity (for [chapter [:chapter-1 :chapter-2 :chapter-3 :chapter-4 :chapter-5]] [[(doto (make-button (if (chapter chapters) (saves/chapter-name chapter) "---") :chapter (chapter chapters)) (text-button! :set-disabled (nil? (chapter chapters)))) :height 56 :pad-bottom 4 :width 400] :row])))] (make-table [[(doto (scroll-pane chapter-table (skin "ui/ui.json")) (scroll-pane! :set-fade-scroll-bars false)) :height 240 :width 472] :row [ (make-button "Back" :key :back) :height 56]]))) (defn settings-menu [] (let [fullscreen-button (make-checkbox "Fullscreen" (utils/is-fullscreen?) :key :toggle-fullscreen) cameraman-button (make-checkbox "Handheld Camera" (@utils/settings :camera-man? true) :key :camera-man?)] (assoc (make-table [[(make-label "Music" label-color) :height 40] :row [(make-slider (:music-volume @utils/settings) :key :music-volume-slider) :width 240] :row [(make-label "FX" label-color) :height 40 :width 200] :row [(make-slider (:sound-volume @utils/settings) :key :sound-volume-slider) :width 240] :row [fullscreen-button :height 56 :width 240] :row [cameraman-button :height 56 :width 280] :row [ (make-button "Back" :key :back) :height 56 :width 240]]) :fullscreen-button fullscreen-button))) (defn get-selected-save [entities [x y]] (first (filter (every-pred :save #(utils/intersects? % [x y])) (vals entities)))) (defn saves-table [entities ^Stage stage] (let [group (doto (ButtonGroup.) (.setMaxCheckCount 1) (.setMinCheckCount 0) (.setUncheckLast true)) [cell-w cell-h] [218 161] make-cell (fn [image] [image :width cell-w :pad 4 4 0 0 :height cell-h]) make-save-screenshot (fn [{:keys [screenshot name id state blurb] :as save}] (let [btn (doto (assoc (image-button (skin "ui/ui.json")) :save save :name name) (image-button! :set-size cell-w cell-h) (image-button! :add (doto (Group. ) (.setSize (- cell-w 16) (- cell-h 16) ) (.setOrigin (/ (- cell-w 16) 2) (/ (- cell-h 16) 2)) (.addActor (:object (doto (image (texture screenshot)) (image! :set-size (- cell-w 16) (- cell-h 16))))) (.addActor (:object (make-label (str "" (or blurb name)) label-color ))) )) save-object)] (.add group ^ImageButton (:object btn)) (ActorEntity. (:object btn)))) save-screenshots (map make-save-screenshot (:saves-list entities)) grouped-screenshots (->> (partition-all 2 save-screenshots) (mapcat (fn [group] (conj (map make-cell group) :row)))) scroll-pane (doto (scroll-pane (table grouped-screenshots) (skin "ui/ui.json")) (scroll-pane! :set-fade-scroll-bars false))] (.setScrollFocus stage ^ScrollPane (:object scroll-pane)) [scroll-pane :colspan 3 :height 190 :pad-bottom 4 :width 472])) (defn saves-menu [entities stage] (let [save-label (make-label "" label-color) continue-button (doto (make-button "Continue" :key :continue) (text-button! :set-disabled true)) delete-button (doto (make-button "Delete" :key :delete) (text-button! :set-disabled true))] (assoc (make-table [[save-label :colspan 3 :height 55] :row (saves-table entities stage) :row [delete-button :width 150 :height 56] [(make-button "Back" :key :back) :width 150 :height 56] [continue-button :width 150 :height 56]]) :save-label save-label :delete-button delete-button :continue-button continue-button))) (defn confirm-delete [entities] (make-table [[(make-label "Are you sure you want" label-color) :height 32 :colspan 2] :row [(make-label "to delete the save" label-color) :height 32 :colspan 2] :row [(make-label (str "\"" (-> entities :selected-save :name) "\"?") label-color) :height 32 :colspan 2 :pad-bottom 4] :row [(make-button "Keep" :key :dont-delete-button) :width 120 :height 56] [(make-button "Delete" :key :confirm-delete-button) :width 120 :height 56]])) (defscreen title-screen :on-show (fn [screen entities options] (let [screen (utils/setup-viewport screen 1280 960)] (log/info "Starting title screen.") (let [font (utils/get-font "ego/font.fnt") music (utils/make-music "music/intro.ogg") balloon (utils/make-anim "title/balloon.png" [15 30] 0.45 (range 4)) banner-back (utils/make-anim "title/banner-back.png" [180 42] 0.2 [0 1 2 1 0 1 2 1])] (graphics! :set-cursor (utils/cursor "cursor.png" :hourglass)) (let [entities {:background (assoc (utils/get-texture "title/background.png" ) :x 0 :y 0 :scale-x 4 :scale-y 4 :origin-x 0 :origin-y 0 :z 0) :cloud-background (assoc (utils/get-texture "title/clouds.png" ) :x 0 :y 0 :scale-x 4 :scale-y 4 :origin-x 0 :origin-y 0 :z 2) :banner-back (assoc (animation->texture (assoc screen :total-time 0.0) banner-back) :x 580 :y 400 :scale-x 4 :scale-y 4 :anim banner-back :z 3) :quill (doto (assoc (image-button (BaseDrawable.)) :x 1150 :y 4 :scale-x 4 :scale-y 4 :origin-x 0 :origin-y 0 :z 10 :key :quill) (image-button! :add (doto (Group. ) (.addActor (:object (doto (image (utils/get-texture "title/quill.png")) (image! :set-scale 4)))))) save-object (#(utils/add-actor-to-stage screen %))) :logo (assoc (utils/get-texture "title/logo.png" ) :x 0 :y 0 :scale-x 4 :scale-y 4 :origin-x 0 :origin-y 0 :z 6) :fade (assoc (utils/get-texture "black.png") :scale-x 80 :scale-y 80 :opacity 1.0 :origin-x 0 :origin-y 0 :z 100) :flying-ego (assoc (utils/get-texture "ego/flying.png") :left (flip (utils/get-texture "ego/flying.png")) :right (utils/get-texture "ego/flying.png") :scale-x 5 :scale-y 5 :origin-x 2 :origin-y 0 :path (catmull-rom-spline (map #(apply vector-2* %) [[-800 450] [1280 450] [2000 100] [0 100] [-800 300] [1280 300] [2000 450]]) true) :x 450 :y 650 :z 5) :balloon (assoc (animation->texture (assoc screen :total-time 0.0) balloon) :x 100 :y 100 :scale-x 4 :scale-y 4 :anim balloon :path (catmull-rom-spline (map #(apply vector-2* %) [[50 50] [70 100] [100 200] [151 206] [300 225] [480 300] [560 400] [650 440] [700 550] [750 600] [860 650] [950 700] [1030 800] [1280 960] [1300 1000] [-50 1000] [-50 -50]]) true) :z 3) :particle-clouds (assoc (particle-effect "particles/particle-clouds" :reset :start) :x 640 :y 480 :z 1) :ego-jet (assoc (particle-effect "particles/jet" :reset :start) :x 450 :y 650 :z 4) #_#_:toolbox (-> (assoc (nine-patch {:region (:object (utils/get-texture "talk-bg-2.png")) :left 9 :top 9 :right 9 :bottom 9}) :y 58 :width 500 :height 297 :z 7) center) :main-menu (->> (main-menu screen) (utils/add-actor-to-stage screen)) :saves-list (utils/snapshot-screenshots) :save-index 0 :font font :music music :volume 1.0 :copyright (->> (make-label (str "© Digital Bounce House 2016 - v" (advent.version/version)) label-color) (utils/add-actor-to-stage screen)) :tweens {:fade-in (tween/tween :fade-in screen [:fade :opacity] 1.0 0.0 1.0 :finish #(do (graphics! :set-cursor (utils/cursor "cursor.png" :main)) (utils/play-music (:music %)) %) :ease tween/ease-in-quadratic)}} ] (merge entities (:main-menu entities)))))) :on-render (fn [{:keys [^FitViewport viewport] :as screen} entities options] (steam/update) (.apply viewport) (clear!) (let [entities (utils/apply-tweens screen entities (:tweens entities)) entities (fly-ego screen entities) entities (fly-balloon screen entities) entities (update-in entities [:balloon] merge (animation->texture screen (:anim (:balloon entities)))) entities (update-in entities [:banner-back] merge (animation->texture screen (:anim (:banner-back entities))))] (when (:fullscreen-button (:settings-menu entities)) (set-checkbox-state (:fullscreen-button (:settings-menu entities)) (utils/is-fullscreen?))) (music! (:music entities) :set-volume (utils/current-music-volume (:volume entities))) (render! screen (sort-by :z (filter :object (vals entities))) ) entities)) :show-screen (fn [entities] entities) :on-key-up (fn [screen entities options] (when (= (key-code :escape) (:key screen)) (utils/toggle-fullscreen!)) nil) :on-ui-changed (fn [screen entities {:keys [^Actor actor] :as options}] (when-not (get-in entities [:tweens :fade-out]) (let [e (-> actor .getUserObject) actor-key (:key e)] (cond (= :music-volume-slider actor-key) (do (swap! utils/settings assoc :music-volume (slider! e :get-value)) (utils/save-settings!) entities) (= :camera-man? actor-key) (do (swap! utils/settings assoc :camera-man? (check-box! e :is-checked)) (utils/save-settings!) entities) (= :sound-volume-slider actor-key) (do (swap! utils/settings assoc :sound-volume (slider! e :get-value)) (utils/save-settings!) entities) (= :quill actor-key) (-> entities (assoc-in [:tweens :fade-out] (tween/tween :fade-out screen [:fade :opacity] 0.0 1.0 1.0 :finish (fn [entities] (utils/stop-music (:music entities)) (reset! credits/saved-took? nil) (set-screen! @(resolve 'advent.core/advent) credits/credits) entities) :ease tween/ease-in-cubic)) (assoc-in [:tweens :fade-out-music] (tween/tween :fade-out-music screen [:volume] 1.0 0.0 1.0))) (#{:back } actor-key) (-> entities (utils/remove-actor-from-stage :save-menu) (utils/remove-actor-from-stage :chapters-menu) (utils/remove-actor-from-stage :settings-menu) (assoc :main-menu (->> (main-menu screen) (utils/add-actor-to-stage screen)))) (#{:dont-delete-button } actor-key) (as-> entities entities (utils/remove-actor-from-stage entities :confirm-delete) (assoc entities :save-menu (->> (saves-menu entities (:renderer screen)) (utils/add-actor-to-stage screen)))) (= :load actor-key) (as-> entities entities (utils/remove-actor-from-stage entities :main-menu) (assoc entities :save-menu (->> (saves-menu entities (:renderer screen)) (utils/add-actor-to-stage screen)))) (= :chapters actor-key) (-> entities (utils/remove-actor-from-stage :main-menu) (assoc :chapters-menu (->> (chapters-menu ) (utils/add-actor-to-stage screen)))) (= :settings actor-key) (-> entities (utils/remove-actor-from-stage :main-menu) (assoc :settings-menu (->> (settings-menu) (utils/add-actor-to-stage screen)))) (= :toggle-fullscreen actor-key) (utils/toggle-fullscreen!) (= :end-quest actor-key) (quit screen entities) (= :continue-or-start actor-key) (start-playing screen entities (if (= "Begin Quest" (quest-label)) nil (first (utils/snapshot-list)))) (= :continue actor-key) (start-playing screen entities (:selected-save entities)) (= :continue actor-key) (start-playing screen entities (:selected-save entities)) (= :delete actor-key) (as-> entities entities (utils/remove-actor-from-stage entities :save-menu) (assoc entities :confirm-delete (->> (confirm-delete entities) (utils/add-actor-to-stage screen)))) (= :confirm-delete-button actor-key) (do (utils/remove-save (:id (:selected-save entities))) (as-> entities entities (utils/remove-actor-from-stage entities :confirm-delete) (assoc entities :saves-list (utils/snapshot-screenshots)) (assoc entities :save-menu (->> (saves-menu entities (:renderer screen)) (utils/add-actor-to-stage screen))))) (:save e) (do (if (text-button! e :is-checked) (do (label! (-> entities :save-menu :save-label) :set-text (:name (:save e))) (text-button! (-> entities :save-menu :continue-button) :set-disabled false) (when (not= "Autosave"(:name (:save e))) (text-button! (-> entities :save-menu :delete-button) :set-disabled false)) (assoc entities :selected-save (:save e))) (do (label! (-> entities :save-menu :save-label) :set-text "") (text-button! (-> entities :save-menu :continue-button) :set-disabled true) (text-button! (-> entities :save-menu :delete-button) :set-disabled true)))) (:chapter e) (start-playing screen entities (:chapter e)) :else entities)))) :on-resize (fn [{:keys [^FitViewport viewport] :as screen} entities {:keys [width height]}] (.update viewport width height false) nil))