text:
(ns play-clj.core
  (:require [clojure.set]
            [play-clj.entities :as e]
            [play-clj.math :as m]
            [play-clj.utils :as u])
  (:import [com.badlogic.gdx Application Audio Files Game Gdx Graphics Input
            InputMultiplexer InputProcessor Net Preferences Screen]
           [com.badlogic.gdx.audio Sound Music]
           [com.badlogic.gdx.assets AssetManager]
           [com.badlogic.gdx.assets.loaders AsynchronousAssetLoader
            ParticleEffectLoader]
           [com.badlogic.gdx.assets.loaders.resolvers
            InternalFileHandleResolver]
           [com.badlogic.gdx.files FileHandle]
           [com.badlogic.gdx.graphics Camera Color GL20 OrthographicCamera
            PerspectiveCamera Pixmap Pixmap$Format PixmapIO Texture
            VertexAttributes$Usage]
           [com.badlogic.gdx.graphics.g2d Batch ParticleEffect]
           [com.badlogic.gdx.graphics.g3d ModelBatch]
           [com.badlogic.gdx.graphics.glutils ShapeRenderer]
           [com.badlogic.gdx.input GestureDetector
            GestureDetector$GestureListener]
           [com.badlogic.gdx.maps MapLayer MapLayers MapObject MapObjects
            MapProperties]
           [com.badlogic.gdx.maps.tiled TiledMap TiledMapTileLayer
            TiledMapTileLayer$Cell TmxMapLoader]
           [com.badlogic.gdx.maps.tiled.renderers
            BatchTiledMapRenderer
            HexagonalTiledMapRenderer
            IsometricStaggeredTiledMapRenderer
            IsometricTiledMapRenderer
            OrthogonalTiledMapRenderer]
           [com.badlogic.gdx.math Vector2 Vector3]
           [com.badlogic.gdx.scenes.scene2d Actor Stage]
           [com.badlogic.gdx.scenes.scene2d.utils ActorGestureListener
            ChangeListener ClickListener DragListener FocusListener]
           [com.badlogic.gdx.utils ScreenUtils Timer$Task]
           [play_clj.entities BundleEntity ShapeEntity]))

(load "core_basics")
(load "core_cameras")
(load "core_graphics")
(load "core_listeners")
(load "core_utils")

(defn ^:private normalize
  [entities]
  (some->> entities
           list
           flatten
           (remove nil?)
           vec))

(defn ^:private wrapper
  [screen-atom screen-fn]
  (screen-fn))

(defn ^:private reset-changed!
  [e-atom e-old e-new]
  (when (and (not= e-old e-new)
             (compare-and-set! e-atom e-old e-new))
    e-new))

(defn ^:private add-to-timeline!
  [screen-atom entities]
  (let [screen @screen-atom]
    (when (:timeline screen)
      (swap! screen-atom
             update-in
             [:timeline]
             conj
             [(:total-time screen) entities]))))

(defn defscreen*
  [screen entities
   {:keys [on-show on-render on-hide on-pause on-resize on-resume on-timer]
    :as options}]
  (let [execute-fn! (fn [func & {:keys [] :as options}]
                      (when func
                        (let [screen-map (merge @screen options)
                              old-entities @entities]
                          (some->> (with-meta
                                     #(normalize (func screen-map old-entities))
                                     (meta func))
                                   (wrapper screen)
                                   (reset-changed! entities old-entities)
                                   (update-screen! @screen)))))
        execute-fn-on-gl! (fn [& args]
                            (on-gl (apply execute-fn! args)))
        update-fn! (fn [func & args]
                     (apply swap! screen func args))]
    {:screen screen
     :entities entities
     :execute-fn! execute-fn!
     :execute-fn-on-gl! execute-fn-on-gl!
     :update-fn! update-fn!
     :options options
     :show (fn []
             ; if using a physics engine in a REPL, we need to forcibly dispose
             ; the world so it is cleaned up properly
             (some-> @screen :world :object .dispose)
             ; set the initial values in the screen map
             (update-fn! assoc
                         :execute-fn! execute-fn!
                         :execute-fn-on-gl! execute-fn-on-gl!
                         :update-fn! update-fn!
                         :options options
                         :on-timer on-timer
                         :layers nil
                         :input-listeners (input-listeners options execute-fn!)
                         :ui-listeners (ui-listeners options execute-fn!))
             ; run :on-show
             (execute-fn! on-show)
             ; update the physics contact listener if a :world was created
             (some->> (contact-listener @screen options execute-fn!)
                      (update-fn! assoc :contact-listener)
                      update-screen!))
     :render (fn [d]
               (swap! screen update-in [:total-time] #(+ (or %1 0) %2) d)
               (some->> (execute-fn! on-render :delta-time d)
                        (add-to-timeline! screen)))
     :hide #(execute-fn! on-hide)
     :pause #(execute-fn! on-pause)
     :resize (fn [w h]
               (execute-fn! on-resize :width w :height h)
               (update-screen! @screen))
     :resume #(execute-fn! on-resume)}))

(defmacro defscreen
  "Defines a screen, and creates vars for all the functions inside of it. All
functions take a screen map and entities vector as arguments, and return the
entities list at the end with any desired changes. If a function returns nil,
the entities list is not changed.

Below are all the possible screen functions. Some of them get special arguments
via the screen map.

    ; main screen functions
    (defscreen my-screen
      ; the screen first shows
      :on-show
      (fn [screen entities]
        entities)
      ; the screen must be rendered (many times per second)
      :on-render
      (fn [screen entities]
        (println (:delta-time screen)) ; time (ms) elapsed since last frame
        (println (:total-time screen)) ; time (ms) elapsed since first :on-show
        entities)
      ; the screen was replaced
      :on-hide
      (fn [screen entities]
        entities)
      ; the screen was resized
      :on-resize
      (fn [screen entities]
        (println (:width screen)) ; the new width of the screen
        (println (:height screen)) ; the new height of the screen
        entities)
      ; the screen resumed from a paused state
      :on-resume
      (fn [screen entities]
        entities)
      ; the screen paused
      :on-pause
      (fn [screen entities]
        entities)
      ; a timer created with add-timer! executed
      :on-timer
      (fn [screen entities]
        (println (:id screen)) ; the id supplied when the timer was created
        entities))

    ; input functions
    ; Tip: convert :input-x and :input-y to screen coordinates with input->screen,
    ; or just use (game :x) and (game :y) instead
    (defscreen my-screen
      ; a key was pressed
      :on-key-down
      (fn [screen entities]
        (println (:key screen)) ; the key that was pressed (see key-code)
        entities)
      ; a key was typed
      :on-key-typed
      (fn [screen entities]
        (println (:character screen)) ; the character that was pressed
        entities)
      ; a key was released
      :on-key-up
      (fn [screen entities]
        (println (:key screen)) ; the key that was released (see key-code)
        entities)
      ; the mouse was moved without pressing any buttons
      :on-mouse-moved
      (fn [screen entities]
        (println (:input-x screen)) ; the x position of the mouse
        (println (:input-y screen)) ; the y position of the mouse
        entities)
      ; the mouse wheel was scrolled
      :on-scrolled
      (fn [screen entities]
        (println (:amount screen)) ; the amount scrolled
        entities)
      ; the screen was touched or a mouse button was pressed
      :on-touch-down
      (fn [screen entities]
        (println (:input-x screen)) ; the x position of the finger/mouse
        (println (:input-y screen)) ; the y position of the finger/mouse
        (println (:pointer screen)) ; the pointer for the event
        (println (:button screen)) ; the mouse button that was pressed (see button-code)
        entities)
      ; a finger or the mouse was dragged
      :on-touch-dragged
      (fn [screen entities]
        (println (:input-x screen)) ; the x position of the finger/mouse
        (println (:input-y screen)) ; the y position of the finger/mouse
        (println (:pointer screen)) ; the pointer for the event
        entities)
      ; a finger was lifted or a mouse button was released
      :on-touch-up
      (fn [screen entities]
        (println (:input-x screen)) ; the x position of the finger/mouse
        (println (:input-y screen)) ; the y position of the finger/mouse
        (println (:pointer screen)) ; the pointer for the event
        (println (:button screen)) ; the mouse button that was released (see button-code)
        entities))

    ; gesture functions
    (defscreen my-screen
      ; the user dragged over the screen and lifted
      :on-fling
      (fn [screen entities]
        (println (:velocity-x screen)) ; the x-axis velocity (s)
        (println (:velocity-y screen)) ; the y-axis velocity (s)
        (println (:button screen)) ; the mouse button that was pressed (see button-code)
        entities)
      ; the user pressed for a long time
      :on-long-press
      (fn [screen entities]
        (println (:input-x screen)) ; the x position of the finger/mouse
        (println (:input-y screen)) ; the y position of the finger/mouse
        entities)
      ; the user dragged a finger over the screen
      :on-pan
      (fn [screen entities]
        (println (:input-x screen)) ; the x position of the finger/mouse
        (println (:input-y screen)) ; the y position of the finger/mouse
        (println (:delta-x screen)) ; the x-axis distance moved
        (println (:delta-y screen)) ; the y-axis distance moved
        entities)
      ; the user is no longer panning
      :on-pan-stop
      (fn [screen entities]
        (println (:input-x screen)) ; the x position of the finger/mouse
        (println (:input-y screen)) ; the y position of the finger/mouse
        (println (:pointer screen)) ; the pointer for the event
        (println (:button screen)) ; the mouse button that was pressed (see button-code)
        entities)
      ; the user performed a pinch zoom gesture
      :on-pinch
      (fn [screen entities]
        (println (:initial-pointer-1 screen)) ; the start position of finger 1 (see the x and y functions)
        (println (:initial-pointer-2 screen)) ; the start position of finger 2 (see the x and y functions)
        (println (:pointer-1 screen)) ; the end position of finger 1 (see the x and y functions)
        (println (:pointer-2 screen)) ; the end position of finger 2 (see the x and y functions)
        entities)
      ; the user tapped
      :on-tap
      (fn [screen entities]
        (println (:input-x screen)) ; the x position of the finger/mouse
        (println (:input-y screen)) ; the y position of the finger/mouse
        (println (:count screen)) ; the number of taps
        (println (:button screen)) ; the mouse button that was pressed (see button-code)
        entities)
      ; the user performed a pinch zoom gesture
      :on-zoom
      (fn [screen entities]
        (println (:initial-distance screen)) ; the start distance between fingers
        (println (:distance screen)) ; the end distance between fingers
        entities))

    ; 2D physics contact (for play-clj.g2d-physics)
    ; Tip: use first-entity and second-entity to get the entities that are contacting
    (defscreen my-screen
      ; two bodies began to touch
      :on-begin-contact
      (fn [screen entities]
        (println (:contact screen)) ; the Contact - http://libgdx.badlogicgames.com/nightlies/docs/api/com/badlogic/gdx/physics/box2d/Contact.html
        entities)
      ; two bodies ceased to touch
      :on-end-contact
      (fn [screen entities]
        (println (:contact screen)) ; the Contact - http://libgdx.badlogicgames.com/nightlies/docs/api/com/badlogic/gdx/physics/box2d/Contact.html
        entities)
      ; called between each use of `step!` before the collision is processed
      :on-pre-solve
      (fn [screen entities]
        (println (:contact screen)) ; the Contact - http://libgdx.badlogicgames.com/nightlies/docs/api/com/badlogic/gdx/physics/box2d/Contact.html
        (println (:impulse screen)) ; the ContactImpulse - http://libgdx.badlogicgames.com/nightlies/docs/api/com/badlogic/gdx/physics/box2d/ContactImpulse.html
        entities)
      ; called between each use of `step!` after the collision is processed
      :on-post-solve
      (fn [screen entities]
        (println (:contact screen)) ; the Contact - http://libgdx.badlogicgames.com/nightlies/docs/api/com/badlogic/gdx/physics/box2d/Contact.html
        (println (:old-manifold screen)) ; the Manifold - http://libgdx.badlogicgames.com/nightlies/docs/api/com/badlogic/gdx/physics/box2d/Manifold.html
        entities))

    ; 3D physics contact (for play-clj.g3d-physics)
    ; Tip: use first-entity and second-entity to get the entities that are contacting
    (defscreen my-screen
      ; two bodies began to touch
      :on-begin-contact
      (fn [screen entities]
        (println (:first-body screen)) ; the first btCollisionObject - http://bulletphysics.org/Bullet/BulletFull/classbtCollisionObject.html
        (println (:second-body screen)) ; the second btCollisionObject - http://bulletphysics.org/Bullet/BulletFull/classbtCollisionObject.html
        entities)
      ; two bodies ceased to touch
      :on-end-contact
      (fn [screen entities]
        (println (:first-body screen)) ; the first btCollisionObject - http://bulletphysics.org/Bullet/BulletFull/classbtCollisionObject.html
        (println (:second-body screen)) ; the second btCollisionObject - http://bulletphysics.org/Bullet/BulletFull/classbtCollisionObject.html
        entities))

    ; ui input functions (for play-clj.ui)
    (defscreen my-screen
      ; the ui entity was clicked or changed
      :on-ui-changed
      (fn [screen entities]
        (println (:event screen)) ; the ChangeListener.ChangeEvent - http://libgdx.badlogicgames.com/nightlies/docs/api/com/badlogic/gdx/scenes/scene2d/utils/ChangeListener.ChangeEvent.html
        (println (:actor screen)) ; the Actor - http://libgdx.badlogicgames.com/nightlies/docs/api/com/badlogic/gdx/scenes/scene2d/Actor.html
        entities)
      ; the finger/mouse moved over the ui entity
      :on-ui-enter
      (fn [screen entities]
        (println (:event screen)) ; the InputEvent - http://libgdx.badlogicgames.com/nightlies/docs/api/com/badlogic/gdx/scenes/scene2d/InputEvent.html
        (println (:actor screen)) ; the Actor - http://libgdx.badlogicgames.com/nightlies/docs/api/com/badlogic/gdx/scenes/scene2d/Actor.html
        (println (:input-x screen)) ; the x position of the finger/mouse
        (println (:input-y screen)) ; the y position of the finger/mouse
        (println (:pointer screen)) ; the pointer for the event
        entities)
      ; the finger/mouse moved out of the ui entity
      :on-ui-exit
      (fn [screen entities]
        (println (:event screen)) ; the InputEvent - http://libgdx.badlogicgames.com/nightlies/docs/api/com/badlogic/gdx/scenes/scene2d/InputEvent.html
        (println (:actor screen)) ; the Actor - http://libgdx.badlogicgames.com/nightlies/docs/api/com/badlogic/gdx/scenes/scene2d/Actor.html
        (println (:input-x screen)) ; the x position of the finger/mouse
        (println (:input-y screen)) ; the y position of the finger/mouse
        (println (:pointer screen)) ; the pointer for the event
        entities)
      ; the finger/mouse went down on the ui entity
      :on-ui-touch-down
      (fn [screen entities]
        (println (:event screen)) ; the InputEvent - http://libgdx.badlogicgames.com/nightlies/docs/api/com/badlogic/gdx/scenes/scene2d/InputEvent.html
        (println (:input-x screen)) ; the x position of the finger/mouse
        (println (:input-y screen)) ; the y position of the finger/mouse
        (println (:pointer screen)) ; the pointer for the event
        (println (:button screen)) ; the mouse button that was pressed (see button-code)
        entities)
      ; the finger/mouse moved anywhere
      :on-ui-touch-dragged
      (fn [screen entities]
        (println (:event screen)) ; the InputEvent - http://libgdx.badlogicgames.com/nightlies/docs/api/com/badlogic/gdx/scenes/scene2d/InputEvent.html
        (println (:input-x screen)) ; the x position of the finger/mouse
        (println (:input-y screen)) ; the y position of the finger/mouse
        (println (:pointer screen)) ; the pointer for the event
        entities)
      ; the finger/mouse went up anywhere
      :on-ui-touch-up
      (fn [screen entities]
        (println (:event screen)) ; the InputEvent - http://libgdx.badlogicgames.com/nightlies/docs/api/com/badlogic/gdx/scenes/scene2d/InputEvent.html
        (println (:input-x screen)) ; the x position of the finger/mouse
        (println (:input-y screen)) ; the y position of the finger/mouse
        (println (:pointer screen)) ; the pointer for the event
        (println (:button screen)) ; the mouse button that was released (see button-code)
        entities))

    ; ui drag functions (for play-clj.ui)
    (defscreen my-screen
      :on-ui-drag
      (fn [screen entities]
        (println (:event screen)) ; the InputEvent - http://libgdx.badlogicgames.com/nightlies/docs/api/com/badlogic/gdx/scenes/scene2d/InputEvent.html
        (println (:input-x screen)) ; the x position of the finger/mouse
        (println (:input-y screen)) ; the y position of the finger/mouse
        (println (:pointer screen)) ; the pointer for the event
        entities)
      :on-ui-drag-start
      (fn [screen entities]
        (println (:event screen)) ; the InputEvent - http://libgdx.badlogicgames.com/nightlies/docs/api/com/badlogic/gdx/scenes/scene2d/InputEvent.html
        (println (:input-x screen)) ; the x position of the finger/mouse
        (println (:input-y screen)) ; the y position of the finger/mouse
        (println (:pointer screen)) ; the pointer for the event
        entities)
      :on-ui-drag-stop
      (fn [screen entities]
        (println (:event screen)) ; the InputEvent - http://libgdx.badlogicgames.com/nightlies/docs/api/com/badlogic/gdx/scenes/scene2d/InputEvent.html
        (println (:input-x screen)) ; the x position of the finger/mouse
        (println (:input-y screen)) ; the y position of the finger/mouse
        (println (:pointer screen)) ; the pointer for the event
        entities))

    ; ui focus functions (for play-clj.ui)
    (defscreen my-screen
      :on-ui-keyboard-focus-changed
      (fn [screen entities]
        (println (:event screen)) ; the FocusListener.FocusEvent - http://libgdx.badlogicgames.com/nightlies/docs/api/com/badlogic/gdx/scenes/scene2d/utils/FocusListener.FocusEvent.html
        (println (:actor screen)) ; the Actor - http://libgdx.badlogicgames.com/nightlies/docs/api/com/badlogic/gdx/scenes/scene2d/Actor.html
        (println (:focused? screen)) ; whether it is focused
        entities)
      :on-ui-scroll-focus-changed
      (fn [screen entities]
        (println (:event screen)) ; the FocusListener.FocusEvent - http://libgdx.badlogicgames.com/nightlies/docs/api/com/badlogic/gdx/scenes/scene2d/utils/FocusListener.FocusEvent.html
        (println (:actor screen)) ; the Actor - http://libgdx.badlogicgames.com/nightlies/docs/api/com/badlogic/gdx/scenes/scene2d/Actor.html
        (println (:focused? screen)) ; whether it is focused
        entities))

    ; ui gesture functions (for play-clj.ui)
    (defscreen my-screen
      ; the user dragged a finger over the screen and lifted it
      :on-ui-fling
      (fn [screen entities]
        (println (:event screen)) ; the InputEvent - http://libgdx.badlogicgames.com/nightlies/docs/api/com/badlogic/gdx/scenes/scene2d/InputEvent.html
        (println (:velocity-x screen)) ; the x-axis velocity (s)
        (println (:velocity-y screen)) ; the y-axis velocity (s)
        (println (:button screen)) ; the mouse button that was pressed (see button-code)
        entities)
      ; the user pressed
      :on-ui-long-press
      (fn [screen entities]
        (println (:actor screen)) ; the Actor - http://libgdx.badlogicgames.com/nightlies/docs/api/com/badlogic/gdx/scenes/scene2d/Actor.html
        (println (:input-x screen)) ; the x position of the finger
        (println (:input-y screen)) ; the y position of the finger
        entities)
      ; the user dragged a finger over the screen
      :on-ui-pan
      (fn [screen entities]
        (println (:event screen)) ; the InputEvent - http://libgdx.badlogicgames.com/nightlies/docs/api/com/badlogic/gdx/scenes/scene2d/InputEvent.html
        (println (:input-x screen)) ; the x position of the finger
        (println (:input-y screen)) ; the y position of the finger
        (println (:delta-x screen)) ; the x-axis distance moved
        (println (:delta-y screen)) ; the y-axis distance moved
        entities)
      ; the user is no longer panning
      :on-ui-pan-stop
      (fn [screen entities]
        (println (:event screen)) ; the InputEvent - http://libgdx.badlogicgames.com/nightlies/docs/api/com/badlogic/gdx/scenes/scene2d/InputEvent.html
        (println (:input-x screen)) ; the x position of the finger
        (println (:input-y screen)) ; the y position of the finger
        (println (:pointer screen)) ; the pointer for the event
        (println (:button screen)) ; the mouse button that was pressed (see button-code)
        entities)
      ; the user performed a pinch zoom gesture
      :on-ui-pinch
      (fn [screen entities]
        (println (:event screen)) ; the InputEvent - http://libgdx.badlogicgames.com/nightlies/docs/api/com/badlogic/gdx/scenes/scene2d/InputEvent.html
        (println (:initial-pointer-1 screen)) ; the start position of finger 1 (see the x and y functions)
        (println (:initial-pointer-2 screen)) ; the start position of finger 2 (see the x and y functions)
        (println (:pointer-1 screen)) ; the end position of finger 1 (see the x and y functions)
        (println (:pointer-2 screen)) ; the end position of finger 2 (see the x and y functions)
        entities)
      ; the user tapped
      :on-ui-tap
      (fn [screen entities]
        (println (:event screen)) ; the InputEvent - http://libgdx.badlogicgames.com/nightlies/docs/api/com/badlogic/gdx/scenes/scene2d/InputEvent.html
        (println (:input-x screen)) ; the x position of the finger
        (println (:input-y screen)) ; the y position of the finger
        (println (:count screen)) ; the number of taps
        (println (:button screen)) ; the mouse button that was pressed (see button-code)
        entities)
      ; the user performed a pinch zoom gesture
      :on-ui-zoom
      (fn [screen entities]
        (println (:event screen)) ; the InputEvent - http://libgdx.badlogicgames.com/nightlies/docs/api/com/badlogic/gdx/scenes/scene2d/InputEvent.html
        (println (:initial-distance screen)) ; the start distance between fingers
        (println (:distance screen)) ; the end distance between fingers
        entities))"
  [n & options]
  (let [s (format "Unexpected value in (defscreen %s). You need to give it
keywords and functions in pairs."
                  (str n))]
    (assert (even? (count options)) s)
    (assert (= 0 (count (remove keyword? (keys (apply hash-map options))))) s))
  `(let [fn-syms# (->> (for [[k# v#] ~(apply hash-map options)]
                         [k# (intern *ns* (symbol (str '~n "-" (name k#))) v#)])
                       (into {}))
         map-sym# (symbol (str '~n "-map"))
         screen# (deref (or (resolve map-sym#)
                            (intern *ns* map-sym# (atom {}))))
         entities-sym# (symbol (str '~n "-entities"))
         entities# (deref (or (resolve entities-sym#)
                              (intern *ns* entities-sym# (atom []))))]
     (def ~n (defscreen* screen# entities# fn-syms#))))

(defn defgame*
  [{:keys [on-create]}]
  (proxy [Game] []
    (create []
      (when on-create
        (on-create this)))))

(defmacro defgame
  "Defines a game. This should only be called once."
  [n & {:keys [] :as options}]
  `(defonce ~n (defgame* ~options)))

(defn set-screen!
  "Creates and displays a screen for the `game-object`, using one or more
`screen-objects` in the order they were provided.

    (set-screen! my-game main-screen text-screen)"
  [^Game game-object & screen-objects]
  (doseq [screen screen-objects]
    (assert (every? #(fn? (get screen %))
                    [:show :render :hide :pause :resize :resume])
            "Attempted to set an invalid screen."))
  (let [run-fn! (fn [k & args]
                  (doseq [screen screen-objects]
                    (apply (get screen k) args)))]
    (.setScreen game-object
                (reify Screen
                  (show [this]
                    (input! :set-input-processor (InputMultiplexer.))
                    (run-fn! :show)
                    (doseq [{:keys [screen]} screen-objects]
                      (doseq [[_ listener] (:input-listeners @screen)]
                        (add-input! listener))
                      (when-let [renderer (:renderer @screen)]
                        (when (instance? Stage renderer)
                          (add-input! renderer)))))
                  (render [this d] (run-fn! :render d))
                  (hide [this] (run-fn! :hide))
                  (pause [this] (run-fn! :pause))
                  (resize [this w h] (run-fn! :resize w h))
                  (resume [this] (run-fn! :resume))
                  (dispose [this])))))

(defn set-screen-wrapper!
  "Sets a function that wraps around all screen functions, allowing you to
handle errors and perform other custom actions each time they run.

    ; default behavior
    (set-screen-wrapper! (fn [screen-atom screen-fn]
                           (screen-fn)))
    ; if there is an error, print it out and switch to a blank screen
    ; (this is useful because it makes error recovery easier in a REPL)
    (set-screen-wrapper! (fn [screen-atom screen-fn]
                           (try (screen-fn)
                             (catch Exception e
                               (.printStackTrace e)
                               (set-screen! my-game blank-screen)))))"
  [wrapper-fn]
  (intern 'play-clj.core 'wrapper wrapper-fn))

(defn update!
  "Runs the equivalent of `(swap! screen-atom assoc ...)`, where `screen-atom`
is the atom storing the screen map behind the scenes. Returns the updated
`screen` map.

    (update! screen :renderer (stage))"
  [screen & args]
  (doto (apply (:update-fn! screen) assoc args)
    update-screen!))

(defn screen!
  "Runs a function defined in another screen. You may optionally pass a series
of key-value pairs, which will be given to the function via its screen map.

    (screen! my-other-screen :on-show)
    (screen! my-other-screen :on-change-color :color :blue)"
  [screen-object fn-name & options]
  (let [execute-fn! (:execute-fn! screen-object)
        screen-fn (-> screen-object :options (get fn-name))]
    (apply execute-fn! screen-fn options)
    nil))


text:
(ns play-clj.math
  (:require [play-clj.utils :as u])
  (:import [com.badlogic.gdx.math Bezier Bresenham2 BSpline CatmullRomSpline
            Circle ConvexHull DelaunayTriangulator EarClippingTriangulator
            Ellipse FloatCounter Frustum GridPoint2 GridPoint3 Matrix3 Matrix4
            Plane Polygon Polyline Quaternion Rectangle Vector2 Vector3
            WindowedMean]
           [com.badlogic.gdx.math.collision BoundingBox Ray Segment Sphere]))

; static methods/fields

(defmacro geometry!
  "Calls a single static method on [GeometryUtils](http://libgdx.badlogicgames.com/nightlies/docs/api/com/badlogic/gdx/math/GeometryUtils.html)."
  [k & options]
  `(~(u/gdx-field :math :GeometryUtils (u/key->camel k)) ~@options))

(defmacro interpolation
  "Returns a static class in [Interpolation](http://libgdx.badlogicgames.com/nightlies/docs/api/com/badlogic/gdx/math/Interpolation.html).

    (interpolation :bounce)"
  [k]
  (u/gdx-class :math :Interpolation (u/key->pascal k)))

(defmacro intersector!
  "Calls a single static method on [Intersector](http://libgdx.badlogicgames.com/nightlies/docs/api/com/badlogic/gdx/math/Intersector.html).

    (intersector! :is-point-in-triangle 0 1 0 0 1 2 3 0)"
  [k & options]
  `(~(u/gdx-field :math :Intersector (u/key->camel k)) ~@options))

(defmacro math!
  "Calls a single static method on [MathUtils](http://libgdx.badlogicgames.com/nightlies/docs/api/com/badlogic/gdx/math/MathUtils.html).

    (math! :ceil 0.1)"
  [k & options]
  `(~(u/gdx-field :math :MathUtils (u/key->camel k)) ~@options))

(defmacro plane-side
  "Returns a static field in [Plane.PlaneSide](http://libgdx.badlogicgames.com/nightlies/docs/api/com/badlogic/gdx/math/Plane.PlaneSide.html).

    (plane-side :back)"
  [k]
  (u/gdx-field :math "Plane$PlaneSide" (u/key->pascal k)))

; bezier

(defn bezier*
  ([]
   (Bezier.))
  ([points]
   (Bezier. (into-array points) 0 (count points))))

(defmacro bezier
  "Returns a [Bezier](http://libgdx.badlogicgames.com/nightlies/docs/api/com/badlogic/gdx/math/Bezier.html)."
  [points & options]
  `(u/calls! ^Bezier (bezier* ~points) ~@options))

(defmacro bezier!
  "Calls a single method on a `bezier`."
  [object k & options]
  `(u/call! ^Bezier ~object ~k ~@options))

; bresenham2

(defn bresenham-2*
  []
  (Bresenham2.))

(defmacro bresenham-2
  "Returns a [Bresenham2](http://libgdx.badlogicgames.com/nightlies/docs/api/com/badlogic/gdx/math/Bresenham2.html)."
  [& options]
  `(u/calls! ^Bresenham2 (bresenham-2*) ~@options))

(defmacro bresenham-2!
  "Calls a single method on a `bresenham-2`."
  [object k & options]
  `(u/call! ^Bresenham2 ~object ~k ~@options))

; b-spline

(defn b-spline*
  ([]
   (BSpline.))
  ([points degree cont?]
   (BSpline. (into-array points) degree cont?)))

(defmacro b-spline
  "Returns a [BSpline](http://libgdx.badlogicgames.com/nightlies/docs/api/com/badlogic/gdx/math/BSpline.html)."
  [points degree cont? & options]
  `(u/calls! ^BSpline (b-spline* ~points ~degree ~cont?) ~@options))

(defmacro b-spline!
  "Calls a single method on a `b-spline`."
  [object k & options]
  `(u/call! ^BSpline ~object ~k ~@options))

; catmull-rom-spline

(defn catmull-rom-spline*
  ([]
   (CatmullRomSpline.))
  ([points cont?]
   (CatmullRomSpline. (into-array points) cont?)))

(defmacro catmull-rom-spline
  "Returns a [CatmullRomSpline](http://libgdx.badlogicgames.com/nightlies/docs/api/com/badlogic/gdx/math/CatmullRomSpline.html)."
  [points cont? & options]
  `(u/calls! ^CatmullRomSpline (catmull-rom-spline* ~points ~cont?) ~@options))

(defmacro catmull-rom-spline!
  "Calls a single method on a `catmull-rom-spline`."
  [object k & options]
  `(u/call! ^CatmullRomSpline ~object ~k ~@options))

; circle

(defn circle*
  ([]
   (Circle.))
  ([x y radius]
   (Circle. x y radius)))

(defmacro circle
  "Returns a [Circle](http://libgdx.badlogicgames.com/nightlies/docs/api/com/badlogic/gdx/math/Circle.html)."
  [x y radius & options]
  `(u/calls! ^Circle (circle* ~x ~y ~radius) ~@options))

(defmacro circle!
  "Calls a single method on a `circle`."
  [object k & options]
  `(u/call! ^Circle ~object ~k ~@options))

; convex-hull

(defn convex-hull*
  []
  (ConvexHull.))

(defmacro convex-hull
  "Returns a [ConvexHull](http://libgdx.badlogicgames.com/nightlies/docs/api/com/badlogic/gdx/math/ConvexHull.html)."
  [& options]
  `(u/calls! ^ConvexHull (convex-hull*) ~@options))

(defmacro convex-hull!
  "Calls a single method on a `convex-hull`."
  [object k & options]
  `(u/call! ^ConvexHull ~object ~k ~@options))

; delaunay-triangulator

(defn delaunay-triangulator*
  []
  (DelaunayTriangulator.))

(defmacro delaunay-triangulator
  "Returns a [DelaunayTriangulator](http://libgdx.badlogicgames.com/nightlies/docs/api/com/badlogic/gdx/math/DelaunayTriangulator.html)."
  [& options]
  `(u/calls! ^DelaunayTriangulator (delaunay-triangulator*) ~@options))

(defmacro delaunay-triangulator!
  "Calls a single method on a `delaunay-triangulator`."
  [object k & options]
  `(u/call! ^DelaunayTriangulator ~object ~k ~@options))

; ear-clipping-triangulator

(defn ear-clipping-triangulator*
  []
  (EarClippingTriangulator.))

(defmacro ear-clipping-triangulator
  "Returns an [EarClippingTriangulator](http://libgdx.badlogicgames.com/nightlies/docs/api/com/badlogic/gdx/math/EarClippingTriangulator.html)."
  [& options]
  `(u/calls! ^EarClippingTriangulator (ear-clipping-triangulator*) ~@options))

(defmacro ear-clipping-triangulator!
  "Calls a single method on a `ear-clipping-triangulator`."
  [object k & options]
  `(u/call! ^EarClippingTriangulator ~object ~k ~@options))

; ellipse

(defn ellipse*
  ([]
   (Ellipse.))
  ([x y width height]
   (Ellipse. x y width height)))

(defmacro ellipse
  "Returns an [Ellipse](http://libgdx.badlogicgames.com/nightlies/docs/api/com/badlogic/gdx/math/Ellipse.html)."
  [x y width height & options]
  `(u/calls! ^Ellipse (ellipse* ~x ~y ~width ~height) ~@options))

(defmacro ellipse!
  "Calls a single method on an `ellipse`."
  [object k & options]
  `(u/call! ^Ellipse ~object ~k ~@options))

; float-counter

(defn float-counter*
  [window-size]
  (FloatCounter. window-size))

(defmacro float-counter
  "Returns a [FloatCounter](http://libgdx.badlogicgames.com/nightlies/docs/api/com/badlogic/gdx/math/FloatCounter.html)."
  [window-size & options]
  `(u/calls! ^FloatCounter (float-counter* ~window-size) ~@options))

(defmacro float-counter!
  "Calls a single method on a `float-counter`."
  [object k & options]
  `(u/call! ^FloatCounter ~object ~k ~@options))

; frustum

(defn frustum*
  []
  (Frustum.))

(defmacro frustum
  "Returns a [Frustum](http://libgdx.badlogicgames.com/nightlies/docs/api/com/badlogic/gdx/math/Frustum.html)."
  [& options]
  `(u/calls! ^Frustum (frustum*) ~@options))

(defmacro frustum!
  "Calls a single method on a `frustum`."
  [object k & options]
  `(u/call! ^Frustum ~object ~k ~@options))

; grid-point-2

(defn grid-point-2*
  [x y]
  (GridPoint2. x y))

(defmacro grid-point-2
  "Returns a [GridPoint2](http://libgdx.badlogicgames.com/nightlies/docs/api/com/badlogic/gdx/math/GridPoint2.html)."
  [x y & options]
  `(u/calls! ^GridPoint2 (grid-point-2* ~x ~y) ~@options))

(defmacro grid-point-2!
  "Calls a single method on a `grid-point-2`."
  [object k & options]
  `(u/call! ^GridPoint2 ~object ~k ~@options))

; grid-point-3

(defn grid-point-3*
  [x y z]
  (GridPoint3. x y z))

(defmacro grid-point-3
  "Returns a [GridPoint3](http://libgdx.badlogicgames.com/nightlies/docs/api/com/badlogic/gdx/math/GridPoint3.html)."
  [x y z & options]
  `(u/calls! ^GridPoint3 (grid-point-3* ~x ~y ~z) ~@options))

(defmacro grid-point-3!
  "Calls a single method on a `grid-point-3`."
  [object k & options]
  `(u/call! ^GridPoint3 ~object ~k ~@options))

; matrix-3

(defn matrix-3*
  ([]
   (Matrix3.))
  ([values]
   (Matrix3. values)))

(defmacro matrix-3
  "Returns a [Matrix3](http://libgdx.badlogicgames.com/nightlies/docs/api/com/badlogic/gdx/math/Matrix3.html)."
  [values & options]
  `(u/calls! ^Matrix3 (matrix-3* ~values) ~@options))

(defmacro matrix-3!
  "Calls a single method on a `matrix-3`."
  [object k & options]
  `(u/call! ^Matrix3 ~object ~k ~@options))

; matrix-4

(defn matrix-4*
  ([]
   (Matrix4.))
  ([^floats values]
   (Matrix4. values)))

(defmacro matrix-4
  "Returns a [Matrix4](http://libgdx.badlogicgames.com/nightlies/docs/api/com/badlogic/gdx/math/Matrix4.html)."
  [values & options]
  `(u/calls! ^Matrix4 (matrix-4* ~values) ~@options))

(defmacro matrix-4!
  "Calls a single method on a `matrix-4`."
  [object k & options]
  `(u/call! ^Matrix4 ~object ~k ~@options))

; plane

(defn plane*
  ([arg1 arg2]
   (Plane. arg1 arg2))
  ([^Vector3 p1 ^Vector3 p2 ^Vector3 p3]
   (Plane. p1 p2 p3)))

(defmacro plane
  "Returns a [Plane](http://libgdx.badlogicgames.com/nightlies/docs/api/com/badlogic/gdx/math/Plane.html)."
  [args & options]
  `(u/calls! ^Plane (apply plane* ~args) ~@options))

(defmacro plane!
  "Calls a single method on a `plane`."
  [object k & options]
  `(u/call! ^Plane ~object ~k ~@options))

; polygon

(defn polygon*
  ([]
   (Polygon.))
  ([vertices]
   (Polygon. vertices)))

(defmacro polygon
  "Returns a [Polygon](http://libgdx.badlogicgames.com/nightlies/docs/api/com/badlogic/gdx/math/Polygon.html)."
  [vertices & options]
  `(u/calls! ^Polygon (polygon* ~vertices) ~@options))

(defmacro polygon!
  "Calls a single method on a `polygon`."
  [object k & options]
  `(u/call! ^Polygon ~object ~k ~@options))

; polyline

(defn polyline*
  ([]
   (Polyline.))
  ([vertices]
   (Polyline. vertices)))

(defmacro polyline
  "Returns a [Polyline](http://libgdx.badlogicgames.com/nightlies/docs/api/com/badlogic/gdx/math/Polyline.html)."
  [vertices & options]
  `(u/calls! ^Polyline (polyline* ~vertices) ~@options))

(defmacro polyline!
  "Calls a single method on a `polyline`."
  [object k & options]
  `(u/call! ^Polyline ~object ~k ~@options))

; quaternion

(defn quaternion*
  ([]
   (Quaternion.))
  ([w x y z]
   (Quaternion. w x y z)))

(defmacro quaternion
  "Returns a [Quaternion](http://libgdx.badlogicgames.com/nightlies/docs/api/com/badlogic/gdx/math/Quaternion.html)."
  [w x y z & options]
  `(u/calls! ^Quaternion (quaternion* ~w ~x ~y ~z) ~@options))

(defmacro quaternion!
  "Calls a single method on a `quaternion`."
  [object k & options]
  `(u/call! ^Quaternion ~object ~k ~@options))

; rectangle

(defn rectangle*
  ([]
   (Rectangle.))
  ([x y width height]
   (Rectangle. x y width height)))

(defmacro rectangle
  "Returns a [Rectangle](http://libgdx.badlogicgames.com/nightlies/docs/api/com/badlogic/gdx/math/Rectangle.html)."
  [x y width height & options]
  `(u/calls! ^Rectangle (rectangle* ~x ~y ~width ~height) ~@options))

(defmacro rectangle!
  "Calls a single method on a `rectangle`."
  [object k & options]
  `(u/call! ^Rectangle ~object ~k ~@options))

; vector-2

(defn vector-2*
  ([]
   (Vector2.))
  ([x y]
   (Vector2. x y)))

(defmacro vector-2
  "Returns a [Vector2](http://libgdx.badlogicgames.com/nightlies/docs/api/com/badlogic/gdx/math/Vector2.html).
The values can be retrieved with `x` and `y`, and modified with `x!` and `y!`."
  [x y & options]
  `(u/calls! ^Vector2 (vector-2* ~x ~y) ~@options))

(defmacro vector-2!
  "Calls a single method on a `vector-2`. If you're trying to modify the values,
see `x!` and `y!`."
  [object k & options]
  `(u/call! ^Vector2 ~object ~k ~@options))

; vector-3

(defn vector-3*
  ([]
   (Vector3.))
  ([x y z]
   (Vector3. x y z)))

(defmacro vector-3
  "Returns a [Vector3](http://libgdx.badlogicgames.com/nightlies/docs/api/com/badlogic/gdx/math/Vector3.html).
The values can be retrieved with `x`, `y` and `z`, and modified with `x!`, `y!`,
and `z!`."
  [x y z & options]
  `(u/calls! ^Vector3 (vector-3* ~x ~y ~z) ~@options))

(defmacro vector-3!
  "Calls a single method on a `vector-3`. If you're trying to modify the values,
see `x!`, `y!`, and `z!`."
  [object k & options]
  `(u/call! ^Vector3 ~object ~k ~@options))

; windowed-mean

(defn windowed-mean*
  [window-size]
  (WindowedMean. window-size))

(defmacro windowed-mean
  "Returns a [WindowedMean](http://libgdx.badlogicgames.com/nightlies/docs/api/com/badlogic/gdx/math/WindowedMean.html)."
  [window-size & options]
  `(u/calls! ^WindowedMean (windowed-mean* ~window-size) ~@options))

(defmacro windowed-mean!
  "Calls a single method on a `windowed-mean`."
  [object k & options]
  `(u/call! ^WindowedMean ~object ~k ~@options))

; bounding-box

(defn bounding-box*
  ([]
   (BoundingBox.))
  ([box]
   (BoundingBox. box))
  ([min max]
   (BoundingBox. min max)))

(defmacro bounding-box
  "Returns a [BoundingBox](http://libgdx.badlogicgames.com/nightlies/docs/api/com/badlogic/gdx/math/collision/BoundingBox.html)."
  [min max & options]
  `(u/calls! ^BoundingBox (bounding-box* ~min ~max) ~@options))

(defmacro bounding-box!
  "Calls a single method on a `bounding-box`."
  [object k & options]
  `(u/call! ^BoundingBox ~object ~k ~@options))

; ray

(defn ray*
  [origin direction]
  (Ray. origin direction))

(defmacro ray
  "Returns a [Ray](http://libgdx.badlogicgames.com/nightlies/docs/api/com/badlogic/gdx/math/collision/Ray.html)."
  [origin direction & options]
  `(u/calls! ^Ray (ray* ~origin ~direction) ~@options))

(defmacro ray!
  "Calls a single method on a `ray`."
  [object k & options]
  `(u/call! ^Ray ~object ~k ~@options))

; segment

(defn segment*
  ([a-x a-y a-z b-x b-y b-z]
   (Segment. a-x a-y a-z b-x b-y b-z))
  ([a b]
   (Segment. a b)))

(defmacro segment
  "Returns a [Segment](http://libgdx.badlogicgames.com/nightlies/docs/api/com/badlogic/gdx/math/collision/Segment.html)."
  [a b & options]
  `(u/calls! ^Segment (segment* ~a ~b) ~@options))

(defmacro segment!
  "Calls a single method on a `segment`."
  [object k & options]
  `(u/call! ^Segment ~object ~k ~@options))

; sphere

(defn sphere*
  [center radius]
  (Sphere. center radius))

(defmacro sphere
  "Returns a [Sphere](http://libgdx.badlogicgames.com/nightlies/docs/api/com/badlogic/gdx/math/collision/Sphere.html)."
  [center radius & options]
  `(u/calls! ^Sphere (sphere* ~center ~radius) ~@options))

(defmacro sphere!
  "Calls a single method on a `sphere`."
  [object k & options]
  `(u/call! ^Sphere ~object ~k ~@options))

repo_name: oakes/play-clj

repo_language: Clojure

mime_type: text/plain


text:
(ns play-clj.utils
  (:require [clojure.string :as s])
  (:import [com.badlogic.gdx.assets AssetManager]
           [com.badlogic.gdx.utils Array ArrayMap]))

; misc

(defn throw-key-not-found
  [k]
  (throw (Exception. (str "The keyword " k " is not found."))))

(defn get-obj
  [obj k]
  (if (map? obj)
    (or (get obj k)
        (throw-key-not-found k))
    obj))

; assets

(def ^:dynamic *asset-manager* nil)

(defn load-asset
  [path type]
  (when-let [^AssetManager am *asset-manager*]
    (.load am path type)
    (.finishLoading am)
    (.get am path type)))

; timers

(def ^:dynamic *timers* nil)

(defn track-timers!
  []
  (intern 'play-clj.utils '*timers* (atom #{})))

(defn stop-timers!
  []
  (when *timers*
    (doseq [t (deref *timers*)]
      (.stop t))
    (reset! *timers* #{})))

; converting keys

(def ^:const main-package "com.badlogic.gdx")

(defn ^:private split-key
  [k]
  (-> k name (s/split #"-")))

(defn ^:private join-keys
  [k-list]
  (->> k-list (map name) (s/join ".")))

(defn key->upper
  "Returns a string based on keyword `k` with upper case and underscores."
  [k]
  (->> (split-key k)
       (map s/upper-case)
       (s/join "_")))

(defn key->pascal
  "Returns a string based on keyword `k` with pascal case and no delimiters."
  [k]
  (->> (split-key k)
       (map s/capitalize)
       (s/join "")))

(defn key->camel
  "Returns a string based on keyword `k` with camel case and no delimiters."
  [k]
  (let [parts (split-key k)]
    (->> (rest parts)
         (map s/capitalize)
         (cons (first parts))
         (s/join ""))))

(defn key->method
  "Returns a symbol based on keyword `k` formatted as a method call."
  [k]
  (symbol (str "." (key->camel k))))

; classes/methods/fields

(defn ^:private add-divider
  [args divider]
  (let [[a1 a2] (take-last 2 args)]
    (conj (vec (drop-last 2 args)) (str (name a1) divider (name a2)))))

(defn gdx
  "Returns a fully-qualified libGDX symbol."
  [& args]
  (symbol (str main-package "." (join-keys args))))

(defn gdx-field
  "Returns a fully-qualified libGDX static method or field."
  [& args]
  (apply gdx (add-divider args "/")))

(defn gdx-class
  "Returns a fully-qualified libGDX static class."
  [& args]
  (apply gdx (add-divider args "$")))

; java interop

(defmacro call!
  "Calls method `k` of `obj` with `args`.

    (call! \"I'm a string\" :index-of \"s\")"
  [obj k & args]
  `(~(key->method k) ~obj ~@args))

(defn create-method-calls
  [calls args]
  (let [method-name (first args)
        [my-args rest-args] (split-with #(not (keyword? %)) (rest args))]
    (if method-name
      (create-method-calls (conj calls `(~(key->method method-name) ~@my-args))
                           rest-args)
      calls)))

(defmacro calls!
  "Calls methods on `obj`.

    (calls! (java.util.ArrayList.) :add \"I'm a string\" :add \"So am I\")"
  [obj & args]
  `(doto ~obj ~@(create-method-calls [] args)))

(defn create-field-setters
  [obj {:keys [] :as args}]
  (map (fn [[k v]]
         `(set! (. ~obj ~(symbol (key->camel k))) ~v))
       args))

(defmacro fields!
  "Sets fields on `obj` (it is important that `obj` is a symbol, because when
the macro expands it will use it several times, and you probably don't want a
new object to be created each time a field is set).

    (fields! obj
             :active true
             :angle 2
             :awake true
             :fixed-rotation false)"
  [obj & args]
  `(do ~@(create-field-setters obj args)
     ~obj))

; data structures

(defn gdx-array*
  [clj-arr]
  (Array. (into-array clj-arr)))

(defmacro gdx-array
  "Returns an [Array](http://libgdx.badlogicgames.com/nightlies/docs/api/com/badlogic/gdx/utils/Array.html).

    (gdx-array [1 2 3 4])"
  [clj-arr & options]
  `(calls! ^Array (gdx-array* ~clj-arr) ~@options))

(defmacro gdx-array!
  "Calls a single method on a `gdx-array`."
  [object k & options]
  `(call! ^Array ~object ~k ~@options))

(defn gdx-array-map*
 [clj-map]
 (let [gdx-map (ArrayMap.)]
   (doseq [[k v] clj-map]
     (.put gdx-map k v))
   gdx-map))

(defmacro gdx-array-map
  "Returns an [ArrayMap](http://libgdx.badlogicgames.com/nightlies/docs/api/com/badlogic/gdx/utils/ArrayMap.html).

    (gdx-array-map {:key-1 1 :key-2 2})"
  [clj-map & options]
  `(calls! ^ArrayMap (gdx-array-map* ~clj-map) ~@options))

(defmacro gdx-array-map!
  "Calls a single method on a `gdx-array-map`."
  [object k & options]
  `(call! ^ArrayMap ~object ~k ~@options))


text:
(in-ns 'play-clj.core)

(defmacro pixmap-format
  "Returns a static field from [Pixmap.Format](http://libgdx.badlogicgames.com/nightlies/docs/api/com/badlogic/gdx/graphics/Pixmap.Format.html).

    (pixmap-format :alpha)"
  [k]
  (u/gdx-field :graphics "Pixmap$Format" (u/key->pascal k)))

(defn pixmap*
  ([^String path]
   (or (u/load-asset path Pixmap)
       (Pixmap. (files! :internal path))))
  ([width height fmt]
   (Pixmap. width height fmt)))

(defmacro pixmap
  "Returns a [Pixmap](http://libgdx.badlogicgames.com/nightlies/docs/api/com/badlogic/gdx/graphics/Pixmap.html).

    (pixmap \"image.png\")"
  [path & options]
  `(let [^Pixmap object# (pixmap* ~path)]
     (u/calls! object# ~@options)))

(defmacro pixmap!
  "Calls a single method on a `pixmap`.

    (pixmap! object :dispose)"
  [object k & options]
  `(let [^Pixmap object# ~object]
     (u/call! object# ~k ~@options)))

(defmacro shape-type
  "Returns a static field from [ShapeRenderer.ShapeType](http://libgdx.badlogicgames.com/nightlies/docs/api/com/badlogic/gdx/graphics/glutils/ShapeRenderer.ShapeType.html).

    (shape-type :filled)"
  [k]
  (u/gdx-field :graphics :glutils "ShapeRenderer$ShapeType" (u/key->pascal k)))

(defn shape*
  ([]
   (ShapeEntity. (ShapeRenderer.)))
  ([max-vertices]
   (ShapeEntity. (ShapeRenderer. max-vertices))))

(defmacro shape
  "Returns an entity based on [ShapeRenderer](http://libgdx.badlogicgames.com/nightlies/docs/api/com/badlogic/gdx/graphics/glutils/ShapeRenderer.html).
You may pass in a type (see `shape-type`) or an existing `shape` entity that you
want to modify.

A `shape` can draw multiple sub-shapes internally, allowing you to create more
complicated shapes. If you use `assoc` to set the overall :x and :y of the
`shape`, each sub-shape's x and y position will be relative to it.

    ; create a green and red rectangle
    (shape :filled
           :set-color (color :green)
           :rect 0 0 10 30
           :set-color (color :red)
           :rect 10 0 10 30)
    ; create an empty shape, then set it to a green rectangle
    (shape (shape :filled)
           :set-color (color :green)
           :rect 0 0 10 30)
    ; create a green rectangle at 10,10 and rotate it 45 degrees
    (assoc (shape :filled
                  :set-color (color :green)
                  :rect 0 0 10 30)
           :x 10
           :y 10
           :angle 45)"
  [type & options]
  (assert (empty? (clojure.set/intersection #{:begin :end} (set options)))
          "No need to call :begin or :end, because it's done for you.")
  `(let [entity# ~(if (keyword? type)
                    `(assoc (shape*) :type (shape-type ~type))
                    type)
         ^ShapeRenderer object# (u/get-obj entity# :object)]
     (assoc entity# :draw! (fn [] (u/calls! object# ~@options)))))

(defmacro shape!
  "Calls a single method on a `shape`."
  [entity k & options]
  `(let [^ShapeRenderer object# (u/get-obj ~entity :object)]
     (u/call! object# ~k ~@options)))

(defn shape?
  "Returns true if `entity` is a `shape`."
  [entity]
  (instance? ShapeRenderer (u/get-obj entity :object)))

; tiled maps

(defn tiled-map*
  ([]
   (TiledMap.))
  ([^String path]
   (or (u/load-asset path TiledMap)
       (.load (TmxMapLoader.) path))))

(defmacro tiled-map
  "Returns a [TiledMap](http://libgdx.badlogicgames.com/nightlies/docs/api/com/badlogic/gdx/maps/tiled/TiledMap.html).
Normally, you don't need to use this directly."
  [s & options]
  `(u/calls! ^TiledMap (tiled-map* ~s) ~@options))

(defmacro tiled-map!
  "Calls a single method on a `tiled-map`.

    (tiled-map! screen :get-layers)"
  [screen k & options]
  `(let [^BatchTiledMapRenderer renderer# (u/get-obj ~screen :renderer)]
     (u/call! ^TiledMap (.getMap renderer#) ~k ~@options)))

(defn tiled-map-layer*
  ([width height tile-width tile-height]
   (TiledMapTileLayer. width height tile-width tile-height))
  ([screen layer]
   (if (instance? MapLayer layer)
       layer
       (let [layers (-> ^BatchTiledMapRenderer (u/get-obj screen :renderer)
                        .getMap
                        .getLayers)]
         (if (number? layer)
           (.get layers (int layer))
           (.get layers (str layer)))))))

(defmacro tiled-map-layer
  "Returns a [TiledMapTileLayer](http://libgdx.badlogicgames.com/nightlies/docs/api/com/badlogic/gdx/maps/tiled/TiledMapTileLayer.html)
from the tiled map in `screen` that matches `layer`.

    (tiled-map-layer screen \"water\")"
  [screen layer & options]
  `(let [^TiledMapTileLayer object# (tiled-map-layer* ~screen ~layer)]
     (u/calls! object# ~@options)))

(defmacro tiled-map-layer!
  "Calls a single method on a `tiled-map-layer`.

    (tiled-map-layer! (tiled-map-layer screen \"water\")
                      :set-cell 0 0 nil)"
  [object k & options]
  `(u/call! ^TiledMapTileLayer (cast TiledMapTileLayer ~object) ~k ~@options))

(defn tiled-map-cell*
  ([]
   (TiledMapTileLayer$Cell.))
  ([^TiledMapTileLayer layer x y]
   (.getCell layer x y)))

(defmacro tiled-map-cell
  "Returns a [TiledMapTileLayer.Cell](http://libgdx.badlogicgames.com/nightlies/docs/api/com/badlogic/gdx/maps/tiled/TiledMapTileLayer.Cell.html)
from the `layer` at position `x` and `y`.

    (tiled-map-cell (tiled-map-layer screen \"water\") 0 0)"
  [layer x y & options]
  `(let [^TiledMapTileLayer$Cell object# (tiled-map-cell* ~layer ~x ~y)]
     (u/calls! object# ~@options)))

(defmacro tiled-map-cell!
  "Calls a single method on a `tiled-map-cell`.

    (-> (tiled-map-layer screen \"water\")
        (tiled-map-cell 0 0)
        (tiled-map-cell! :set-rotation 90))"
  [object k & options]
  `(u/call! ^TiledMapTileLayer$Cell ~object ~k ~@options))

(defn map-layers*
  ([]
   (MapLayers.))
  ([screen]
   (let [^BatchTiledMapRenderer renderer (u/get-obj screen :renderer)]
     (-> renderer .getMap .getLayers))))

(defmacro map-layers
  "Returns the [MapLayers](http://libgdx.badlogicgames.com/nightlies/docs/api/com/badlogic/gdx/maps/MapLayers.html)
in the tiled map in `screen`.

    (map-layers screen)"
  [screen & options]
  `(let [^MapLayers object# (map-layers* ~screen)]
     (u/calls! object# ~@options)))

(defmacro map-layers!
  "Calls a single method on a `map-layers`.

    (map-layers! object :remove (map-layer screen \"objects\"))"
  [object k & options]
  `(u/call! ^MapLayers ~object ~k ~@options))

(defn map-layer*
  ([]
   (MapLayer.))
  ([screen layer]
   (tiled-map-layer* screen layer)))

(defmacro map-layer
  "Returns a [MapLayer](http://libgdx.badlogicgames.com/nightlies/docs/api/com/badlogic/gdx/maps/MapLayer.html)
from the tiled map in `screen` that matches `layer`. This is necessary for
non-tile layers, like object and image layers.

    (map-layer screen \"objects\")"
  [screen layer & options]
  `(let [^MapLayer object# (map-layer* ~screen ~layer)]
     (u/calls! object# ~@options)))

(defmacro map-layer!
  "Calls a single method on a `map-layer`.

    (map-layer! (map-layer screen \"objects\")
                :set-visible false)"
  [object k & options]
  `(u/call! ^MapLayer ~object ~k ~@options))

(defn map-layer-names
  "Returns a list with strings cooresponding to the name of each layer in the
tiled map in `screen`."
  [screen]
  (doall
    (for [^MapLayer layer (map-layers screen)]
      (.getName layer))))

(defn map-objects*
  ([]
   (MapObjects.))
  ([^MapLayer layer]
   (.getObjects layer)))

(defmacro map-objects
  "Returns the [MapObjects](http://libgdx.badlogicgames.com/nightlies/docs/api/com/badlogic/gdx/maps/MapObjects.html)
in the `layer`.

    (map-objects layer)"
  [^MapLayer layer & options]
  `(let [^MapObjects object# (map-objects* ~layer)]
     (u/calls! object# ~@options)))

(defmacro map-objects!
  "Calls a single method on a `map-objects`.

    (map-objects! (map-objects layer) :remove (map-object layer 0))"
  [object k & options]
  `(u/call! ^MapObjects ~object ~k ~@options))

(defn ^:private map-object-init
  [k]
  (u/gdx :maps :objects (str (u/key->pascal k) "MapObject.")))

(defn map-object*
  []
  (MapObject.))

(defmacro map-object
  "Returns a subclass of [MapObject](http://libgdx.badlogicgames.com/nightlies/docs/api/com/badlogic/gdx/maps/MapObject.html).

    (map-object :circle)"
  [type & options]
  `(let [^MapObject object# (~(map-object-init type))]
     (u/calls! object# ~@options)))

(defmacro map-object!
  "Calls a single method on a `map-object`.

    (map-object! (map-object :rectangle) :get-rectangle)"
  [object k & options]
  `(u/call! ^MapObject ~object ~k ~@options))

; coordinates

(defn screen->input
  "Returns a map with the provided x,y,z values converted from screen to input
coordinates.

    (screen->input screen {:x 10 :y 10 :z 0})
    (screen->input screen 10 10)
    (screen->input screen 10 10 0)"
  ([screen {:keys [x y z] :or {x 0 y 0 z 0} :as entity}]
   (try
     (let [^Camera camera (u/get-obj screen :camera)
           coords (m/vector-3 x y z)]
       (.project camera coords)
       (assoc entity
              :x (. coords x)
              :y (. coords y)
              :z (. coords z)))
      ; if there's no camera, just flip the y axis
     (catch Exception _
       (assoc entity :y (- (game :height) y)))))
  ([screen x y]
   (screen->input screen {:x x :y y}))
  ([screen x y z]
   (screen->input screen {:x x :y y :z z})))

(defn input->screen
  "Returns a map with the provided x,y,z values converted from input to screen
coordinates.

    (input->screen screen {:x 10 :y 10 :z 0})
    (input->screen screen 10 10)
    (input->screen screen 10 10 0)"
  ([screen {:keys [x y z] :or {x 0 y 0 z 0} :as entity}]
   (try
     (let [^Camera camera (u/get-obj screen :camera)
           coords (m/vector-3 x y z)]
       (.unproject camera coords)
       (assoc entity
              :x (. coords x)
              :y (. coords y)
              :z (. coords z)))
      ; if there's no camera, just flip the y axis
     (catch Exception _
       (assoc entity :y (- (game :height) y)))))
  ([screen x y]
   (input->screen screen {:x x :y y}))
  ([screen x y z]
   (input->screen screen {:x x :y y :z z})))

(defn ^:private tiled-map-prop
  [screen]
  (let [^BatchTiledMapRenderer renderer (u/get-obj screen :renderer)
        ^MapProperties prop (-> renderer .getMap .getProperties)]
    {:unit-scale (.getUnitScale renderer)
     :tile-width (.get prop "tilewidth")
     :tile-height (.get prop "tileheight")
     :width (.get prop "width")
     :height (.get prop "height")}))

(defn screen->isometric
  "Returns a map with the provided x,y values converted from screen to isometric
map coordinates.

    (screen->isometric screen {:x 64 :y 32})
    (screen->isometric screen 64 32)"
  ([screen {:keys [x y] :or {x 0 y 0} :as entity}]
   (let [{:keys [unit-scale tile-width tile-height]} (tiled-map-prop screen)
         half-tile-width (/ (* tile-width unit-scale) 2)
         half-tile-height (/ (* tile-height unit-scale) 2)]
     (assoc entity
            :x (/ (- (/ x half-tile-width)
                     (/ y half-tile-height))
                  2)
            :y (/ (+ (/ y half-tile-height)
                     (/ x half-tile-width))
                  2))))
  ([screen x y]
   (screen->isometric screen {:x x :y y})))

(defn isometric->screen
  "Returns a map with the provided x,y values converted from isometric map to
screen coordinates.

    (isometric->screen screen {:x 2 :y 1})
    (isometric->screen screen 2 1)"
  ([screen {:keys [x y] :as entity}]
   (let [{:keys [unit-scale tile-width tile-height]} (tiled-map-prop screen)
         half-tile-width (/ (* tile-width unit-scale) 2)
         half-tile-height (/ (* tile-height unit-scale) 2)]
     (assoc entity
            :x (+ (* x half-tile-width)
                  (* y half-tile-width))
            :y (+ (* -1 x half-tile-height)
                  (* y half-tile-height)))))
  ([screen x y]
   (isometric->screen screen {:x x :y y})))

; renderers

(defn orthogonal-tiled-map*
  [path unit]
  (OrthogonalTiledMapRenderer. ^TiledMap (if (string? path)
                                           (tiled-map* path)
                                           path)
                               (float unit)))

(defmacro orthogonal-tiled-map
  "Returns an [OrthogonalTiledMapRenderer](http://libgdx.badlogicgames.com/nightlies/docs/api/com/badlogic/gdx/maps/tiled/renderers/OrthogonalTiledMapRenderer.html)
with the tiled map file at `path` and `unit` scale.

    (orthogonal-tiled-map \"level1.tmx\" (/ 1 8))"
  [path unit & options]
  `(u/calls! ^OrthogonalTiledMapRenderer (orthogonal-tiled-map* ~path ~unit)
             ~@options))

(defmacro orthogonal-tiled-map!
  "Calls a single method on an `orthogonal-tiled-map`."
  [screen k & options]
  `(let [^OrthogonalTiledMapRenderer object# (u/get-obj ~screen :renderer)]
     (u/call! object# ~k ~@options)))

(defn isometric-tiled-map*
  [path unit]
  (IsometricTiledMapRenderer. ^TiledMap (if (string? path)
                                          (tiled-map* path)
                                          path)
                              (float unit)))

(defmacro isometric-tiled-map
  "Returns an [IsometricTiledMapRenderer](http://libgdx.badlogicgames.com/nightlies/docs/api/com/badlogic/gdx/maps/tiled/renderers/IsometricTiledMapRenderer.html)
with the tiled map file at `path` and `unit` scale.

    (isometric-tiled-map \"level1.tmx\" (/ 1 8))"
  [path unit & options]
  `(u/calls! ^IsometricTiledMapRenderer (isometric-tiled-map* ~path ~unit)
             ~@options))

(defmacro isometric-tiled-map!
  "Calls a single method on an `isometric-tiled-map`."
  [screen k & options]
  `(let [^IsometricTiledMapRenderer object# (u/get-obj ~screen :renderer)]
     (u/call! object# ~k ~@options)))

(defn isometric-staggered-tiled-map*
  [path unit]
  (IsometricStaggeredTiledMapRenderer. ^TiledMap (if (string? path)
                                                   (tiled-map* path)
                                                   path)
                                       (float unit)))

(defmacro isometric-staggered-tiled-map
  "Returns an [IsometricStaggeredTiledMapRenderer](http://libgdx.badlogicgames.com/nightlies/docs/api/com/badlogic/gdx/maps/tiled/renderers/IsometricStaggeredTiledMapRenderer.html)
with the tiled map file at `path` and `unit` scale.

    (isometric-staggered-tiled-map \"level1.tmx\" (/ 1 8))"
  [path unit & options]
  `(u/calls! ^IsometricStaggeredTiledMapRenderer
             (isometric-staggered-tiled-map* ~path ~unit)
             ~@options))

(defmacro isometric-staggered-tiled-map!
  "Calls a single method on an `isometric-staggered-tiled-map`."
  [screen k & options]
  `(let [^IsometricStaggeredTiledMapRenderer object#
         (u/get-obj ~screen :renderer)]
     (u/call! object# ~k ~@options)))

(defn hexagonal-tiled-map*
  [path unit]
  (HexagonalTiledMapRenderer. ^TiledMap (if (string? path)
                                          (tiled-map* path)
                                          path)
                              (float unit)))

(defmacro hexagonal-tiled-map
  "Returns a [HexagonalTiledMapRenderer](http://libgdx.badlogicgames.com/nightlies/docs/api/com/badlogic/gdx/maps/tiled/renderers/HexagonalTiledMapRenderer.html)
with the tiled map file at `path` and `unit` scale.

    (hexagonal-tiled-map \"level1.tmx\" (/ 1 8))"
  [path unit & options]
  `(u/calls! ^HexagonalTiledMapRenderer (hexagonal-tiled-map* ~path ~unit)
             ~@options))

(defmacro hexagonal-tiled-map!
  "Calls a single method on a `hexagonal-tiled-map`."
  [screen k & options]
  `(let [^HexagonalTiledMapRenderer object# (u/get-obj ~screen :renderer)]
     (u/call! object# ~k ~@options)))

(defn stage*
  []
  (Stage.))

(defmacro stage
  "Returns a [Stage](http://libgdx.badlogicgames.com/nightlies/docs/api/com/badlogic/gdx/scenes/scene2d/Stage.html).

    (stage)"
  [& options]
  `(u/calls! ^Stage (stage*) ~@options))

(defmacro stage!
  "Calls a single method on a `stage`."
  [screen k & options]
  `(let [^Stage object# (u/get-obj ~screen :renderer)]
     (u/call! object# ~k ~@options)))

; draw

(defmulti draw!
  (fn [screen _] (-> screen :renderer class)))

(defmethod draw! BatchTiledMapRenderer
  [{:keys [^BatchTiledMapRenderer renderer] :as screen} entities]
  (let [^Batch batch (.getBatch renderer)]
    (.begin batch)
    (doseq [entity entities]
      (e/draw! entity screen batch))
    (.end batch))
  entities)

(defmethod draw! Stage
  [{:keys [^Stage renderer] :as screen} entities]
  (let [^Batch batch (.getBatch renderer)]
    (.begin batch)
    (doseq [{:keys [additive? opacity] :as entity} entities]
      (when additive?
        (.setBlendFunction ^Batch batch (gl :gl-src-alpha) (gl :gl-one)))
      (.setColor batch (color 1 1 1 (or opacity 1.0)))
      (e/draw! entity screen batch)
      (.setColor batch (color 1 1 1 1))
      (when additive?
        (.setBlendFunction ^Batch batch (gl :gl-src-alpha) (gl :gl-one-minus-src-alpha))))
    (.end batch))
  entities)

(defmethod draw! ModelBatch
  [{:keys [^ModelBatch renderer ^Camera camera] :as screen} entities]
  (.begin renderer camera)
  (doseq [entity entities]
    (e/draw! entity screen nil))
  (.end renderer)
  entities)

; render

(defn render-map!
  "Calls the tiled-map renderer from `screen`, optionally allowing you to
specify which layers to render with or without.

    (render-map! screen :with \"water\" \"grass\")
    (render-map! screen :without \"desert\" \"rocks\")"
  [{:keys [^BatchTiledMapRenderer renderer ^Camera camera] :as screen}
   & [k & layer-names]]
  (when camera (.setView renderer camera))
  (if k
    (let [all-layer-names (map-layer-names screen)]
      ; make sure the layer names exist
      (doseq [n layer-names]
        (when-not (contains? (set all-layer-names) n)
          (throw (Exception. (format "Layer \"%s\" does not exist." n)))))
      ; render with or without the supplied layers
      (->> (case k
             :with (set layer-names)
             :without (clojure.set/difference (set all-layer-names)
                                              (set layer-names))
             (u/throw-key-not-found k))
           (map #(.indexOf ^java.util.List all-layer-names %))
           (sort)
           int-array
           (.render renderer)))
    (.render renderer))
  nil)

(defn render-stage!
  "Calls the stage renderer from `screen`."
  [{:keys [^Stage renderer] :as screen}]
  (doto renderer .act .draw)
  nil)

(defn render!
  "Calls the renderer from `screen` and optionally draws and returns the
`entities`.

    (render! screen entities)"
  ([{:keys [renderer] :as screen}]
   (cond
     (instance? BatchTiledMapRenderer renderer)
     (render-map! screen)
     (instance? Stage renderer)
     (render-stage! screen)))
  ([screen entities]
   (render! screen)
   (draw! screen entities)))

(defn ^:private create-layer
  [^TiledMapTileLayer layer]
  (TiledMapTileLayer. (.getWidth layer)
                      (.getHeight layer)
                      (int (.getTileWidth layer))
                      (int (.getTileHeight layer))))

(defn ^:private isometric?
  [{:keys [renderer] :as screen}]
  (or (instance? IsometricTiledMapRenderer renderer)
      (instance? IsometricStaggeredTiledMapRenderer renderer)))

(defn ^:private split-layer
  [screen layer-name]
  (let [^TiledMapTileLayer l (tiled-map-layer screen layer-name)]
    (reduce (fn [layers {:keys [x y] :as map-tile}]
              (let [screen-tile (if (isometric? screen)
                                  (isometric->screen screen map-tile)
                                  map-tile)
                    new-layer (or (->> layers
                                       (filter #(= (:y screen-tile) (:y %)))
                                       first)
                                  (assoc screen-tile :layer (create-layer l)))]
                (->> (tiled-map-layer! l :get-cell x y)
                     (tiled-map-layer! (:layer new-layer) :set-cell x y))
                (if (contains? (set layers) new-layer)
                  layers
                  (conj layers new-layer))))
            []
            (for [x (range (- (.getWidth l) 1) -1 -1)
                  y (range (- (.getHeight l) 1) -1 -1)]
              {:x x :y y}))))

(defn ^:private sort-by-y
  [entities]
  (sort-by :y #(compare %2 %1) entities))

(defn render-sorted!
  "Draws the specified layer tiles and entities sorted by their position on the
y axis. A custom sort function may be provided. This is primarily intended for
games with isometric tiled maps, where the layer tiles often need to be sorted
to overlap correctly with the entities.

    (render-sorted! screen [\"walls\"] entities)
    (render-sorted! screen #(sort-by :y %) [\"walls\"] entities)"
  ([screen layer-names entities]
   (render-sorted! screen sort-by-y layer-names entities))
  ([{:keys [^BatchTiledMapRenderer renderer
            ^Camera camera
            update-fn!]
     :as screen}
    sort-fn layer-names entities]
   (doseq [ln layer-names]
     (when-not (get-in screen [:layers ln])
       (update-fn! assoc-in [:layers ln] (split-layer screen ln))))
   (when camera (.setView renderer camera))
   (let [^Batch batch (.getBatch renderer)]
     (.begin batch)
     (doseq [entity (->> (map #(get-in screen [:layers %]) layer-names)
                         (apply concat entities)
                         sort-fn)]
       (if-let [layer (:layer entity)]
         (.renderTileLayer renderer layer)
         (e/draw! entity screen batch)))
     (.end batch))
   entities))


text:
(ns play-clj.g2d-physics
  (:require [play-clj.core :as c]
            [play-clj.math :as m]
            [play-clj.utils :as u])
  (:import [com.badlogic.gdx.math MathUtils Vector2]
           [com.badlogic.gdx.physics.box2d Body BodyDef ChainShape CircleShape
            Contact ContactListener EdgeShape Fixture FixtureDef Joint JointDef
            PolygonShape Transform]))

; world

(defn box-2d*
  ([]
   (box-2d* 0 0 true))
  ([gravity-x gravity-y]
   (box-2d* gravity-x gravity-y true))
  ([gravity-x gravity-y sleep?]
   ; use reflection to instantiate in order to avoid the static initializer
   (some-> (try (Class/forName "com.badlogic.gdx.physics.box2d.World")
             (catch Exception _))
           .getDeclaredConstructors
           first
           (.newInstance
             (object-array [(m/vector-2 gravity-x gravity-y) sleep?])))))

(defmacro box-2d
  "Returns a [World](http://libgdx.badlogicgames.com/nightlies/docs/api/com/badlogic/gdx/physics/box2d/World.html).

    (box-2d 0 0)"
  [gravity-x gravity-y & options]
  `(let [object# (box-2d* ~gravity-x ~gravity-y)]
     (u/calls! object# ~@options)))

(defmacro box-2d!
  "Calls a single method on a `box-2d`."
  [screen k & options]
  `(let [object# (u/get-obj ~screen :world)]
     (u/call! object# ~k ~@options)))

; bodies

(defn ^:private body-type
  [k]
  (u/gdx-class :physics :box2d :BodyDef
               (str "BodyType/" (u/key->pascal k) "Body")))

(defmacro body-def
  "Returns a [BodyDef](http://libgdx.badlogicgames.com/nightlies/docs/api/com/badlogic/gdx/physics/box2d/BodyDef.html).

    (body-def :dynamic)"
  [k & options]
  `(let [^BodyDef object# (BodyDef.)]
     (u/fields! object# :type ~(body-type k) ~@options)))

(defmacro body!
  "Calls a single method on a body."
  [entity k & options]
  `(let [^Body object# (u/get-obj ~entity :body)]
     (u/call! object# ~k ~@options)))

(defn add-body!
  "Creates a body from `b-def`, adds it to the `screen` and returns it.

    (add-body! screen (body-def :dynamic))"
  [screen b-def]
  (box-2d! screen :create-body b-def))

(defn ^:private body-x
  [entity]
  (. (body! entity :get-position) x))

(defn ^:private body-y
  [entity]
  (. (body! entity :get-position) y))

(defn ^:private body-angle
  [entity]
  (* (body! entity :get-angle) MathUtils/radiansToDegrees))

(defn ^:private body-origin-x
  [entity]
  (. ^Vector2 (body! entity :get-local-center) x))

(defn ^:private body-origin-y
  [entity]
  (. ^Vector2 (body! entity :get-local-center) y))

(defn body-position!
  "Changes the position of the body in `entity`. The angle is in degrees."
  [entity x y angle]
  (body! entity :set-transform x y (* angle MathUtils/degreesToRadians)))

(defn body-x!
  "Changes the `x` of the body in `entity`."
  [entity x]
  (body-position! entity x (body-y entity) (body-angle entity)))

(defn body-y!
  "Changes the `y` of the body in `entity`."
  [entity y]
  (body-position! entity (body-x entity) y (body-angle entity)))

(defn body-angle!
  "Changes the `angle` (degrees) of the body in `entity`."
  [entity angle]
  (body-position! entity (body-x entity) (body-y entity) angle))

(defn ^:private find-body
  [body entities]
  (some #(try
           (when (= body (u/get-obj % :body)) %)
           (catch Exception _))
        entities))

(defn first-entity
  "Returns the first entity in a contact. May only be used in contact functions
such as :on-begin-contact."
  [screen entities]
  (let [^Contact contact (u/get-obj screen :contact)]
    (-> contact .getFixtureA .getBody (find-body entities))))

(defn second-entity
  "Returns the second entity in a contact. May only be used in contact functions
such as :on-begin-contact."
  [screen entities]
  (let [^Contact contact (u/get-obj screen :contact)]
    (-> contact .getFixtureB .getBody (find-body entities))))

; joints

(defn ^:private joint-init
  [k]
  (u/gdx :physics :box2d :joints (str (u/key->pascal k) "JointDef.")))

(defmacro joint-def
  "Returns a subclass of [JointDef](http://libgdx.badlogicgames.com/nightlies/docs/api/com/badlogic/gdx/physics/box2d/JointDef.html).

    (joint-def :rope)"
  [k & options]
  `(let [object# (~(joint-init k))]
     (u/fields! object# ~@options)))

(defmacro joint!
  "Calls a single method on a joint."
  [object k & options]
  `(u/call! ^Joint ~object ~k ~@options))

(defn add-joint!
  "Adds the `joint` to the `screen` for physics simulations and returns it."
  [screen j-def]
  (box-2d! screen :create-joint j-def))

; fixtures

(defmacro fixture-def
  "Returns a [FixtureDef](http://libgdx.badlogicgames.com/nightlies/docs/api/com/badlogic/gdx/physics/box2d/FixtureDef.html)."
  [& options]
  `(let [^FixtureDef object# (FixtureDef.)]
     (u/fields! object# ~@options)
     object#))

(defmacro fixture!
  "Calls a single method on a fixture."
  [object k & options]
  `(u/call! ^Fixture ~object ~k ~@options))

; shapes

(defn chain-shape*
  []
  (ChainShape.))

(defmacro chain-shape
  "Returns a [ChainShape](http://libgdx.badlogicgames.com/nightlies/docs/api/com/badlogic/gdx/physics/box2d/ChainShape.html)."
  [& options]
  `(u/calls! ^ChainShape (chain-shape*) ~@options))

(defmacro chain-shape!
  "Calls a single method on a `chain-shape`."
  [object k & options]
  `(u/call! ^ChainShape ~object ~k ~@options))

(defn circle-shape*
  []
  (CircleShape.))

(defmacro circle-shape
  "Returns a [CircleShape](http://libgdx.badlogicgames.com/nightlies/docs/api/com/badlogic/gdx/physics/box2d/CircleShape.html)."
  [& options]
  `(u/calls! ^CircleShape (circle-shape*) ~@options))

(defmacro circle-shape!
  "Calls a single method on a `circle-shape`."
  [object k & options]
  `(u/call! ^CircleShape ~object ~k ~@options))

(defn edge-shape*
  []
  (EdgeShape.))

(defmacro edge-shape
  "Returns an [EdgeShape](http://libgdx.badlogicgames.com/nightlies/docs/api/com/badlogic/gdx/physics/box2d/EdgeShape.html)."
  [& options]
  `(u/calls! ^EdgeShape (edge-shape*) ~@options))

(defmacro edge-shape!
  "Calls a single method on an `edge-shape`."
  [object k & options]
  `(u/call! ^EdgeShape ~object ~k ~@options))

(defn polygon-shape*
  []
  (PolygonShape.))

(defmacro polygon-shape
  "Returns a [PolygonShape](http://libgdx.badlogicgames.com/nightlies/docs/api/com/badlogic/gdx/physics/box2d/PolygonShape.html)."
  [& options]
  `(u/calls! ^PolygonShape (polygon-shape*) ~@options))

(defmacro polygon-shape!
  "Calls a single method on a `polygon-shape`."
  [object k & options]
  `(u/call! ^PolygonShape ~object ~k ~@options))

; misc

(defmethod c/contact-listener
  "com.badlogic.gdx.physics.box2d.World"
  [screen
   {:keys [on-begin-contact on-end-contact on-post-solve on-pre-solve]}
   execute-fn!]
  (reify ContactListener
    (beginContact [this c]
      (execute-fn! on-begin-contact :contact c))
    (endContact [this c]
      (execute-fn! on-end-contact :contact c))
    (postSolve [this c i]
      (execute-fn! on-post-solve :contact c :impulse i))
    (preSolve [this c m]
      (execute-fn! on-pre-solve :contact c :old-manifold m))))

(defmethod c/update-physics!
  "com.badlogic.gdx.physics.box2d.World"
  [{:keys [world contact-listener]} & [entities]]
  (.setContactListener world contact-listener)
  (when (and entities (not (.isLocked world)))
    (let [arr (u/gdx-array [])]
      ; remove bodies that no longer exist
      (.getBodies world arr)
      (doseq [body arr]
        (when-not (some #(= body (:body %)) entities)
          (.destroyBody world body)))
      ; remove joints whose bodies no longer exist
      (.getJoints world arr)
      (doseq [^Joint joint arr]
        (when (and (not (some #(= (.getBodyA joint) (:body %)) entities))
                   (not (some #(= (.getBodyB joint) (:body %)) entities)))
          (.destroyJoint world joint))))))

(defn step!
  "Runs the physics simulations for a single frame and optionally returns the
`entities` with their positions updated."
  ([{:keys [world time-step velocity-iterations position-iterations]
     :or {time-step (/ 1 60) velocity-iterations 10 position-iterations 10}
     :as screen}]
   (.step world time-step velocity-iterations position-iterations))
  ([screen entities]
   (step! screen)
   (map (fn [e]
          (if (:body e)
            (assoc e
                   :x (body-x e)
                   :y (body-y e)
                   :angle (body-angle e)
                   :origin-x (body-origin-x e)
                   :origin-y (body-origin-y e))
            e))
        entities)))


text:
(in-ns 'play-clj.core)

; global

(defn ^:private input-processor
  [{:keys [on-key-down on-key-typed on-key-up on-mouse-moved
           on-scrolled on-touch-down on-touch-dragged on-touch-up]}
   execute-fn!]
  (reify InputProcessor
    (keyDown [this k]
      (execute-fn! on-key-down :key k)
      false)
    (keyTyped [this c]
      (execute-fn! on-key-typed :character c)
      false)
    (keyUp [this k]
      (execute-fn! on-key-up :key k)
      false)
    (mouseMoved [this sx sy]
      (execute-fn! on-mouse-moved :input-x sx :input-y sy)
      false)
    (scrolled [this a]
      (execute-fn! on-scrolled :amount a)
      false)
    (touchDown [this sx sy p b]
      (execute-fn! on-touch-down :input-x sx :input-y sy :pointer p :button b)
      false)
    (touchDragged [this sx sy p]
      (execute-fn! on-touch-dragged :input-x sx :input-y sy :pointer p)
      false)
    (touchUp [this sx sy p b]
      (execute-fn! on-touch-up :input-x sx :input-y sy :pointer p :button b)
      false)))

(defmacro input-processor!
  "Calls a single method on the [InputProcessor](http://libgdx.badlogicgames.com/nightlies/docs/api/com/badlogic/gdx/InputProcessor.html)
in the `screen`."
  [screen k & options]
  `(let [listeners# (u/get-obj ~screen :input-listeners)
         ^InputProcessor object# (u/get-obj listeners# :input-processor)]
     (u/call! object# ~k ~@options)))

(defn ^:private gesture-listener
  [{:keys [on-fling on-long-press on-pan on-pan-stop on-pinch on-tap on-zoom]}
   execute-fn!]
  (reify GestureDetector$GestureListener
    (fling [this vx vy b]
      (execute-fn! on-fling :velocity-x vx :velocity-y vy :button b)
      (some? on-fling))
    (longPress [this x y]
      (execute-fn! on-long-press :input-x x :input-y y)
      (some? on-long-press))
    (pan [this x y dx dy]
      (execute-fn! on-pan :input-x x :input-y y :delta-x dx :delta-y dy)
      (some? on-pan))
    (panStop [this x y p b]
      (execute-fn! on-pan-stop :input-x x :input-y y :pointer p :button b)
      (some? on-pan-stop))
    (pinch [this ip1 ip2 p1 p2]
      (execute-fn! on-pinch
                   :initial-pointer-1 ip1 :initial-pointer-2 ip2
                   :pointer-1 p1 :pointer-2 p2)
      (some? on-pinch))
    (tap [this x y c b]
      (execute-fn! on-tap :input-x x :input-y y :count c :button b)
      (some? on-tap))
    (touchDown [this x y p b]
      false)
    (zoom [this id d]
      (execute-fn! on-zoom :initial-distance id :distance d)
      (some? on-zoom))))

(defn ^:private gesture-detector
  [options execute-fn!]
  (proxy [GestureDetector] [(gesture-listener options execute-fn!)]))

(defmacro gesture-detector!
  "Calls a single method on the [GestureDetector](http://libgdx.badlogicgames.com/nightlies/docs/api/com/badlogic/gdx/input/GestureDetector.html)
in the `screen`."
  [screen k & options]
  `(let [listeners# (u/get-obj ~screen :input-listeners)
         ^GestureDetector object# (u/get-obj listeners# :gesture-detector)]
     (u/call! object# ~k ~@options)))

(defn ^:private input-listeners
  [options execute-fn!]
  {:input-processor (input-processor options execute-fn!)
   :gesture-detector (gesture-detector options execute-fn!)})

; ui

(defn ^:private actor-gesture-listener
  [{:keys [on-ui-fling on-ui-long-press on-ui-pan on-ui-pinch
           on-ui-tap on-ui-zoom]}
   execute-fn!]
  (proxy [ActorGestureListener] []
    (fling [e vx vy b]
      (execute-fn! on-ui-fling
                   :event e :velocity-x vx :velocity-y vy :button b))
    (longPress [a x y]
      (execute-fn! on-ui-long-press :actor a :input-x x :input-y y)
      (some? on-ui-long-press))
    (pan [e x y dx dy]
      (execute-fn! on-ui-pan
                   :event e :input-x x :input-y y :delta-x dx :delta-y dy))
    (pinch [e ip1 ip2 p1 p2]
      (execute-fn! on-ui-pinch
                   :event e :initial-pointer-1 ip1 :initial-pointer-2 ip2
                   :pointer1 p1 :pointer2 p2))
    (tap [e x y p b]
      (execute-fn! on-ui-tap
                   :event e :input-x x :input-y y :pointer p :button b))
    ;(touchDown [e x y p b])
    ;(touchUp [e x y p b])
    (zoom [e id d]
      (execute-fn! on-ui-zoom :event e :initial-distance id :distance d))))

(defn ^:private change-listener
  [{:keys [on-ui-changed]} execute-fn!]
  (proxy [ChangeListener] []
    (changed [e a]
      (execute-fn! on-ui-changed :event e :actor a))))

(defn ^:private click-listener
  [{:keys [on-ui-clicked on-ui-enter on-ui-exit
           on-ui-touch-down on-ui-touch-dragged on-ui-touch-up]}
   execute-fn!]
  (proxy [ClickListener] []
    (clicked [e x y]
      (execute-fn! on-ui-clicked :event e :input-x x :input-y y))
    (enter [e x y p a]
      (execute-fn! on-ui-enter
                   :event e :input-x x :input-y y :pointer p :actor a))
    (exit [e x y p a]
      (execute-fn! on-ui-exit
                   :event e :input-x x :input-y y :pointer p :actor a))
    (touchDown [e x y p b]
      (execute-fn! on-ui-touch-down
                   :event e :input-x x :input-y y :pointer p :button b)
      (some? on-ui-touch-down))
    (touchDragged [e x y p]
      (execute-fn! on-ui-touch-dragged
                   :event e :input-x x :input-y y :pointer p))
    (touchUp [e x y p b]
      (execute-fn! on-ui-touch-up
                   :event e :input-x x :input-y y :pointer p :button b))))

(defn ^:private drag-listener
  [{:keys [on-ui-drag on-ui-drag-start on-ui-drag-stop]}
   execute-fn!]
  (proxy [DragListener] []
    ;(touchDown [e x y p b])
    ;(touchDragged [e x y p])
    ;(touchUp [e x y p b])
    (drag [e x y p]
      (execute-fn! on-ui-drag :event e :input-x x :input-y y :pointer p))
    (dragStart [e x y p]
      (execute-fn! on-ui-drag-start :event e :input-x x :input-y y :pointer p))
    (dragStop [e x y p]
      (execute-fn! on-ui-drag-stop :event e :input-x x :input-y y :pointer p))))

(defn ^:private focus-listener
  [{:keys [on-ui-keyboard-focus-changed on-ui-scroll-focus-changed]}
   execute-fn!]
  (proxy [FocusListener] []
    (keyboardFocusChanged [e a f]
      (execute-fn! on-ui-keyboard-focus-changed :event e :actor a :focused? f))
    (scrollFocusChanged [e a f]
      (execute-fn! on-ui-scroll-focus-changed :event e :actor a :focused? f))))

(defn ^:private ui-listeners
  [options execute-fn!]
  {:actor-gesture-listener (actor-gesture-listener options execute-fn!)
   :change-listener (change-listener options execute-fn!)
   :click-listener (click-listener options execute-fn!)
   :drag-listener (drag-listener options execute-fn!)
   :focus-listener (focus-listener options execute-fn!)})

(defmulti contact-listener
  (fn [screen options execute-fn!] (some-> screen :world class .getName))
  :default nil)

(defmethod contact-listener nil [_ _ _])

; update functions

(defn ^:private update-stage!
  ([{:keys [^Stage renderer ^Camera camera] :as screen}]
   (when camera
     (doto (.getViewport renderer)
       (.setCamera camera)
       (.setWorldSize (. camera viewportWidth) (. camera viewportHeight))
       (.update (game :width) (game :height) true))))
  ([{:keys [^Stage renderer ui-listeners]} entities]
   (let [bundle? #(instance? BundleEntity %)
         current (->> entities
                      (mapcat #(tree-seq bundle? :entities %))
                      (map :object)
                      (filter #(instance? Actor %))
                      (into #{}))
           ;; realize this before removing from it, since mutable
         previous (set (.getActors renderer))
         entering (clojure.set/difference current previous)
         exiting (clojure.set/difference previous current)]
     (doseq [^Actor a exiting]
       (.remove a))
     (doseq [^Actor a entering]
       (.addActor renderer a)
       (doseq [[_ listener] ui-listeners]
         (.addListener a listener))))))

(defmulti update-physics!
  (fn [screen & [entities]] (some-> screen :world class .getName))
  :default nil)

(defmethod update-physics! nil [_ & _])

(defn ^:private update-screen!
  ([{:keys [renderer world] :as screen}]
   (when (instance? Stage renderer)
     (update-stage! screen))
   (update-physics! screen))
  ([{:keys [renderer world] :as screen} entities]
   (when (instance? Stage renderer)
     (update-stage! screen entities))
   (update-physics! screen entities)
   entities))


text:
(defproject play-clj/lein-template "1.2.0-SNAPSHOT"
  :description "A template for making play-clj projects"
  :url "https://github.com/oakes/play-clj"
  :license {:name "Public Domain"
            :url "http://unlicense.org/UNLICENSE"}
  :dependencies [[lein-droid "0.4.3"]]
  :resource-paths ["resources"]
  :eval-in-leiningen true)


text:
(ns leiningen.new.play-clj
  (:require [clojure.java.io :as io]
            [leiningen.core.main :as main]
            [leiningen.droid.new :as droid-new]
            [leiningen.new.templates :as t]))

(defn play-clj
  [name & [package-name]]
  (let [render (t/renderer "play-clj")
        lein-droid-render (droid-new/renderer "templates")
        desktop-class-name "desktop-launcher"
        android-class-name "AndroidLauncher"
        package-name (t/sanitize (t/multi-segment (or package-name name)))
        package-prefix (->> (.lastIndexOf package-name ".")
                            (subs package-name 0))
        main-ns (t/sanitize-ns package-name)
        desktop-ns (str main-ns "." desktop-class-name)
        android-ns (str package-name "." android-class-name)
        data {:app-name name
              :game-name (str name "-game")
              :name (t/project-name name)
              :package package-name
              :package-sanitized package-name
              :package-prefix package-prefix
              :desktop-class-name desktop-class-name
              :android-class-name android-class-name
              :activity android-class-name
              :namespace main-ns
              :desktop-namespace desktop-ns
              :android-namespace android-ns
              :path (t/name-to-path main-ns)
              :desktop-path (t/name-to-path desktop-ns)
              :android-path (t/name-to-path android-ns)
              :year (t/year)
              :target-sdk "15"}]
    (t/->files data
               ; main
               ["README.md" (render "README.md" data)]
               [".gitignore" (render "gitignore" data)]
               ; desktop
               ["desktop/project.clj" (render "desktop-project.clj" data)]
               ["desktop/src-common/{{path}}.clj" (render "core.clj" data)]
               ["desktop/src/{{desktop-path}}.clj"
                (render "desktop-launcher.clj" data)]
               "desktop/src-common"
               "desktop/src"
               "desktop/resources"
               ; android
               ["android/project.clj"
                (render "android-project.clj" data)]
               ["android/src/java/{{android-path}}.java"
                (render "AndroidLauncher.java" data)]
               "android/src/clojure"
               ["android/AndroidManifest.template.xml"
                (render "AndroidManifest.xml" data)]
               ["android/res/drawable-hdpi/ic_launcher.png"
                (lein-droid-render "ic_launcher_hdpi.png")]
               ["android/res/drawable-mdpi/ic_launcher.png"
                (lein-droid-render "ic_launcher_mdpi.png")]
               ["android/res/values/strings.xml"
                (lein-droid-render "strings.xml" data)]
               ["android/res/drawable-hdpi/splash_circle.png"
                (lein-droid-render "splash_circle.png")]
               ["android/res/drawable-hdpi/splash_droid.png"
                (lein-droid-render "splash_droid.png")]
               ["android/res/drawable-hdpi/splash_hands.png"
                (lein-droid-render "splash_hands.png")]
               ["android/res/drawable/splash_background.xml"
                (lein-droid-render "splash_background.xml")]
               ["android/res/anim/splash_rotation.xml"
                (lein-droid-render "splash_rotation.xml")]
               ["android/res/layout/splashscreen.xml"
                (lein-droid-render "splashscreen.xml")]
               ["android/src/java/{{path}}/SplashActivity.java"
                (render "SplashActivity.java" data)]
               ; android libgdx.so
               ["android/libs/armeabi/libgdx.so"
                (io/input-stream (io/resource "armeabi/libgdx.so"))]
               ["android/libs/armeabi-v7a/libgdx.so"
                (io/input-stream (io/resource "armeabi-v7a/libgdx.so"))]
               ["android/libs/arm64-v8a/libgdx.so"
                (io/input-stream (io/resource "arm64-v8a/libgdx.so"))]
               ; android libgdx-box2d.so
               ["android/libs/armeabi/libgdx-box2d.so"
                (io/input-stream (io/resource "armeabi/libgdx-box2d.so"))]
               ["android/libs/armeabi-v7a/libgdx-box2d.so"
                (io/input-stream (io/resource "armeabi-v7a/libgdx-box2d.so"))]
               ["android/libs/arm64-v8a/libgdx-box2d.so"
                (io/input-stream (io/resource "arm64-v8a/libgdx-box2d.so"))]
               ; android libgdx-bullet.so
               ["android/libs/armeabi/libgdx-bullet.so"
                (io/input-stream (io/resource "armeabi/libgdx-bullet.so"))]
               ["android/libs/armeabi-v7a/libgdx-bullet.so"
                (io/input-stream (io/resource "armeabi-v7a/libgdx-bullet.so"))]
               ["android/libs/arm64-v8a/libgdx-bullet.so"
                (io/input-stream (io/resource "arm64-v8a/libgdx-bullet.so"))])))


text:
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
          package="{{package-sanitized}}"
          android:versionCode="1"
          android:versionName="1.0" >

  <uses-sdk android:minSdkVersion="{{target-sdk}}" />

  <application
      android:icon="@drawable/ic_launcher"
      android:label="@string/app_name">
    <activity android:name=".SplashActivity"
              android:theme="@android:style/Theme.Translucent.NoTitleBar">
      <intent-filter>
        <action android:name="android.intent.action.MAIN"/>
        <category android:name="android.intent.category.LAUNCHER"/>
      </intent-filter>
    </activity>
    <activity android:name=".{{activity}}">
      <intent-filter>
        <action android:name='{{package-sanitized}}.MAIN'/>
        <category android:name='android.intent.category.DEFAULT'/>
      </intent-filter>
    </activity>
  </application>

</manifest>


text:
(ns {{desktop-namespace}}
  (:require [{{namespace}} :refer :all])
  (:import [com.badlogic.gdx.backends.lwjgl LwjglApplication]
           [org.lwjgl.input Keyboard])
  (:gen-class))

(defn -main
  []
  (LwjglApplication. {{game-name}} "{{app-name}}" 800 600)
  (Keyboard/enableRepeatEvents true))


text:
package {{package}};

import android.app.Activity;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.os.Bundle;
import android.view.animation.Animation;
import android.view.animation.AnimationUtils;
import android.widget.ImageView;
import android.widget.TextView;
import android.util.Log;

import clojure.lang.Symbol;
import clojure.lang.Var;
import clojure.lang.RT;

import {{package}}.R;

public class SplashActivity extends Activity {

    private static boolean firstLaunch = true;
    private static String TAG = "Splash";

    @Override
    public void onCreate(Bundle bundle) {
        super.onCreate(bundle);

        if (firstLaunch) {
            firstLaunch = false;
            setupSplash();
            loadClojure();
        } else {
            proceed();
        }
    }

    public void setupSplash() {
        setContentView(R.layout.splashscreen);

        TextView appNameView = (TextView)findViewById(R.id.splash_app_name);
        appNameView.setText(R.string.app_name);

        Animation rotation = AnimationUtils.loadAnimation(this, R.anim.splash_rotation);
        ImageView circleView = (ImageView)findViewById(R.id.splash_circles);
        circleView.startAnimation(rotation);
    }

    public void proceed() {
        startActivity(new Intent("{{package-sanitized}}.MAIN"));
        finish();
    }

    public void loadClojure() {
        new Thread(new Runnable(){
                @Override
                public void run() {
                    Symbol CLOJURE_MAIN = Symbol.intern("neko.init");
                    Var REQUIRE = RT.var("clojure.core", "require");
                    REQUIRE.invoke(CLOJURE_MAIN);

                    Var INIT = RT.var("neko.init", "init");
                    INIT.invoke(SplashActivity.this.getApplication());

                    try {
                        Class.forName("{{package}}.{{activity}}");
                    } catch (ClassNotFoundException e) {
                        Log.e(TAG, "Failed loading {{activity}}", e);
                    }

                    proceed();
                }
            }).start();
    }
}


text:
package {{package}};

import clojure.lang.RT;
import clojure.lang.Symbol;

import com.badlogic.gdx.backends.android.AndroidApplication;
import com.badlogic.gdx.Game;

public class {{android-class-name}} extends AndroidApplication {
	public void onCreate (android.os.Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
          RT.var("clojure.core", "require").invoke(Symbol.intern("{{namespace}}"));
		try {
			Game game = (Game) RT.var("{{namespace}}", "{{game-name}}").deref();
			initialize(game);
		} catch (Exception e) {
			e.printStackTrace();
		}
	}
}


text:
(ns {{namespace}}
  (:require [play-clj.core :refer :all]
            [play-clj.ui :refer :all]))

(defscreen main-screen
  :on-show
  (fn [screen entities]
    (update! screen :renderer (stage))
    (label "Hello world!" (color :white)))

  :on-render
  (fn [screen entities]
    (clear!)
    (render! screen entities)))

(defgame {{game-name}}
  :on-create
  (fn [this]
    (set-screen! this main-screen)))


text:
(defproject {{app-name}} "0.0.1-SNAPSHOT"
  :description "FIXME: write description"

  :dependencies [[com.badlogicgames.gdx/gdx "1.9.3"]
                 [com.badlogicgames.gdx/gdx-backend-lwjgl "1.9.3"]
                 [com.badlogicgames.gdx/gdx-box2d "1.9.3"]
                 [com.badlogicgames.gdx/gdx-box2d-platform "1.9.3"
                  :classifier "natives-desktop"]
                 [com.badlogicgames.gdx/gdx-bullet "1.9.3"]
                 [com.badlogicgames.gdx/gdx-bullet-platform "1.9.3"
                  :classifier "natives-desktop"]
                 [com.badlogicgames.gdx/gdx-platform "1.9.3"
                  :classifier "natives-desktop"]
                 [org.clojure/clojure "1.7.0"]
                 [play-clj "1.1.1"]]

  :source-paths ["src" "src-common"]
  :javac-options ["-target" "1.6" "-source" "1.6" "-Xlint:-options"]
  :aot [{{desktop-namespace}}]
  :main {{desktop-namespace}})


text:
(defproject {{app-name}} "0.0.1-SNAPSHOT"
  :description "FIXME: write description"

  :dependencies [[com.badlogicgames.gdx/gdx "1.9.3" :use-resources true]
                 [com.badlogicgames.gdx/gdx-backend-android "1.9.3"]
                 [com.badlogicgames.gdx/gdx-box2d "1.9.3"]
                 [com.badlogicgames.gdx/gdx-bullet "1.9.3"]
                 [neko/neko "3.2.0"]
                 [org.clojure-android/clojure "1.7.0-r4" :use-resources true]
                 [play-clj "1.1.1"]]
  :profiles {:dev {:dependencies [[org.clojure-android/tools.nrepl "0.2.6-lollipop"]]
                   :android {:aot :all-with-unused}}
             :release {:android
                       {;; Specify the path to your private
                        ;; keystore and the the alias of the
                        ;; key you want to sign APKs with.
                        ;; :keystore-path "/home/user/.android/private.keystore"
                        ;; :key-alias "mykeyalias"
                        :aot :all}}}

  :android {;; Specify the path to the Android SDK directory either
            ;; here or in your ~/.lein/profiles.clj file.
            ;; :sdk-path "/home/user/path/to/android-sdk/"

            ;; Uncomment this if dexer fails with OutOfMemoryException
            ;; :force-dex-optimize true

            :assets-paths ["../desktop/resources"]
            :native-libraries-paths ["libs"]
            :target-version "{{target-sdk}}"
            :aot-exclude-ns ["clojure.parallel" "clojure.core.reducers"
                             "cljs-tooling.complete" "cljs-tooling.info"
                             "cljs-tooling.util.analysis" "cljs-tooling.util.misc"
                             "cider.nrepl" "cider-nrepl.plugin"]
            :dex-opts ["-JXmx4096M"]}

  :source-paths ["src/clojure" "../desktop/src-common"]
  :java-source-paths ["src/java"]
  :javac-options ["-target" "1.6" "-source" "1.6" "-Xlint:-options"])


text:
## Introduction

A play-clj game in which ... well, that part is up to you.

## Contents

* `android/src` Android-specific code
* `desktop/resources` Images, audio, and other files
* `desktop/src` Desktop-specific code
* `desktop/src-common` Cross-platform game code


text:
package play_clj.g3d_physics;

import clojure.lang.IFn;
import com.badlogic.gdx.physics.bullet.collision.*;

public class ContactListener3D extends ContactListener {
	IFn started, ended;

	public ContactListener3D(IFn started, IFn ended) {
		this.started = started;
		this.ended = ended;
	}

	public void onContactStarted(btCollisionObject a, btCollisionObject b) {
		started.invoke(a, b);
	}

	public void onContactEnded(btCollisionObject a, btCollisionObject b) {
		ended.invoke(a, b);
	}
}


text:
(defproject play-clj-doclet "0.0.1-SNAPSHOT"
  :description "FIXME: write description"
  :dependencies [[hiccup "1.0.5"]
                 [marginalia "0.7.1"]
                 [markdown-clj "0.9.41"]
                 [org.clojure/clojure "1.6.0"]]
  :source-paths ["src/clojure"]
  :java-source-paths ["src/java"]
  :javac-options ["-target" "1.6" "-source" "1.6" "-Xlint:-options"]
  :aot :all)


text:
This doclet generates the play-clj doc site.

## Directions

1. Download and extract the [libGDX source code](https://github.com/libgdx/libgdx/releases)
2. Run `lein uberjar` in this directory
3. Run the following command in this directory:

`javadoc -classpath /path/to/libgdx/gdx/src/:/path/to/libgdx/extensions/gdx-box2d/gdx-box2d/src/ -subpackages com.badlogic -doclet play_clj_doclet.core.Start -docletpath target/play-clj-doclet-0.0.1-SNAPSHOT-standalone.jar`