Screen stack support
- rename "game-state" to "screen" (like "title screen", "options screen", etc) - rename :state to :id - provide helpers to load a screen given a "spec", deserialize a screen back to a spec, manage a screen stack - rework test repl helper to allow setting the screen directly, instead of tweaking game state - have start-game take a spec by default
This commit is contained in:
parent
cc62dfb054
commit
bf41a54aec
|
@ -2,17 +2,21 @@
|
|||
(:require [hottub.slick :as slick]
|
||||
[hottub.gs :as gs]
|
||||
[hottub.resource :as res]
|
||||
[hottub.timeline :as tln]))
|
||||
[hottub.timeline :as tln]
|
||||
[hottub.screen :as scr]))
|
||||
|
||||
(defn -main [& args]
|
||||
(res/start-resource-expiry-thread)
|
||||
(slick/start-game "Test game"
|
||||
{:state :game
|
||||
:gs (gs/update-gs gs/gs-empty
|
||||
(gs/add-index :name)
|
||||
(gs/add-index :type)
|
||||
(gs/set-entity (gs/gen-id) {:type :sprite :x 50 :y 50
|
||||
:image "res/man.png" :name :man}))}))
|
||||
(slick/start-game "Test game" {:id :game}))
|
||||
|
||||
(defmethod scr/screen-from-spec :game [spec]
|
||||
{:gs (gs/update-gs gs/gs-empty
|
||||
(gs/add-index :name)
|
||||
(gs/add-index :type)
|
||||
(gs/set-entity (gs/gen-id) {:type :sprite :x 50 :y 50
|
||||
:image "res/man.png" :name :man}))})
|
||||
|
||||
(defmethod scr/spec-from-screen :game [screen] {:id :game})
|
||||
|
||||
(defmethod slick/update-game :game [state inputtln delta]
|
||||
(assoc state :gs (gs/update-gs (:gs state)
|
||||
|
@ -26,11 +30,14 @@
|
|||
(if-let [image (res/get-image (:image sprite))]
|
||||
(.draw image (:x sprite) (:y sprite))))))
|
||||
|
||||
;;; repl helpers
|
||||
(def ^:dynamic g nil)
|
||||
(def ^:dynamic s nil)
|
||||
(def ^:dynamic *newscreen* nil)
|
||||
|
||||
(defn reload [] (reset! *newscreen* (scr/reload-screen s)))
|
||||
|
||||
(defmethod slick/eval-with-bindings :default [state game f]
|
||||
(binding [g game s state]
|
||||
(binding [g game s state *newscreen* (atom nil)]
|
||||
(let [gsnew (gs/update-gs (:gs state) (f))]
|
||||
(slick/game-setstate! game (assoc state :gs gsnew)))))
|
||||
|
||||
(slick/game-setstate! game (or @*newscreen* (assoc state :gs gsnew))))))
|
||||
|
|
39
src/hottub/screen.clj
Normal file
39
src/hottub/screen.clj
Normal file
|
@ -0,0 +1,39 @@
|
|||
(ns hottub.screen)
|
||||
|
||||
(defmulti screen-from-spec (fn [spec] (:id spec)))
|
||||
(defmulti spec-from-screen (fn [screen] (:id screen)))
|
||||
|
||||
(defn- -screen-from-spec [spec] (assoc (screen-from-spec spec) :id (:id spec)))
|
||||
(defn- -spec-from-screen [screen] (assoc (spec-from-screen screen) :id (:id screen)))
|
||||
|
||||
(defn push-screen [screenold spec]
|
||||
(let [specold (-spec-from-screen screenold)
|
||||
screen (-screen-from-spec spec)]
|
||||
(update-in [::stack] conj specold)))
|
||||
|
||||
(defn pop-screen [screenold]
|
||||
(let [stackold (::stack screenold)
|
||||
specnew (peek stackold)
|
||||
stacknew (pop stackold)
|
||||
screen (-screen-from-spec specnew)]
|
||||
(assoc screen ::stack stacknew)))
|
||||
|
||||
(defn push-modal-screen [screenold spec]
|
||||
(let [screen (-screen-from-spec spec)]
|
||||
(assoc screen ::modal screenold)))
|
||||
|
||||
(defn pop-modal-screen [screenold]
|
||||
(::modal screenold))
|
||||
|
||||
(defn screen-behind-modal [screen]
|
||||
(pop-modal-screen screen))
|
||||
|
||||
(defn update-screen [screenold screennew]
|
||||
(assoc screennew ::stack (::stack screenold)))
|
||||
|
||||
(defn goto-screen [screenold spec]
|
||||
(let [screen (-screen-from-spec spec)]
|
||||
(assoc screen ::stack (::stack screenold))))
|
||||
|
||||
(defn reload-screen [screen]
|
||||
(goto-screen screen (-spec-from-screen screen)))
|
|
@ -8,11 +8,12 @@
|
|||
[hottub.util :as u]
|
||||
[hottub.repl :as repl]
|
||||
[hottub.resource :as res]
|
||||
[hottub.timeline :as tln]))
|
||||
[hottub.timeline :as tln]
|
||||
[hottub.screen :as scr]))
|
||||
|
||||
(defmulti update-game (fn [state input delta] (:state state)))
|
||||
(defmulti render-game (fn [state graphics] (:state state)))
|
||||
(defmulti eval-with-bindings (fn [state game f] (:state state)))
|
||||
(defmulti update-game (fn [screen input delta] (:id screen)))
|
||||
(defmulti render-game (fn [screen graphics] (:id screen)))
|
||||
(defmulti eval-with-bindings (fn [screen game f] (:id screen)))
|
||||
|
||||
(def slick-key-to-key
|
||||
{Input/KEY_0 :0
|
||||
|
@ -119,14 +120,14 @@
|
|||
:state state
|
||||
:extends org.newdawn.slick.BasicGame)
|
||||
|
||||
(defn game-create [title state]
|
||||
(defn game-create [title screen]
|
||||
[[title]
|
||||
{:state (atom state)
|
||||
{:screen (atom screen)
|
||||
:repl (repl/repl-create 9999)
|
||||
:container (atom nil)
|
||||
:input (input-create)}])
|
||||
|
||||
(defn game-state-atom [this] (:state (.state this)))
|
||||
(defn game-screen-atom [this] (:screen (.state this)))
|
||||
(defn game-container [this] @(:container (.state this)))
|
||||
|
||||
(defn game-install-input [this]
|
||||
|
@ -147,7 +148,7 @@
|
|||
(game-install-input this)
|
||||
(repl/repl-start
|
||||
(:repl (.state this))
|
||||
(fn [f] (eval-with-bindings @(game-state-atom this) this f))))
|
||||
(fn [f] (eval-with-bindings @(game-screen-atom this) this f))))
|
||||
|
||||
(defn game-update [this container delta]
|
||||
(doseq [ns-sym (modified-namespaces)]
|
||||
|
@ -158,29 +159,29 @@
|
|||
(.printStackTrace e))))
|
||||
(repl/repl-update (:repl (.state this)))
|
||||
(try
|
||||
(if-let [statenew (update-game @(game-state-atom this) (input-timeline-clear! (:input (.state this))) delta)]
|
||||
(reset! (game-state-atom this) statenew))
|
||||
(if-let [screennew (update-game @(game-screen-atom this) (input-timeline-clear! (:input (.state this))) delta)]
|
||||
(reset! (game-screen-atom this) screennew))
|
||||
(catch Exception e
|
||||
(println "Couldn't update" (:state @(game-state-atom this)))
|
||||
(println "Couldn't update" (:id @(game-screen-atom this)))
|
||||
(.printStackTrace e))))
|
||||
|
||||
(defn game-render [this container graphics]
|
||||
(try
|
||||
(res/gc-expired-resources)
|
||||
(render-game @(game-state-atom this) graphics)
|
||||
(render-game @(game-screen-atom this) graphics)
|
||||
(catch Exception e
|
||||
(println "Couldn't render" (:state @(game-state-atom this)))
|
||||
(println "Couldn't render" (:id @(game-screen-atom this)))
|
||||
(.printStackTrace e))))
|
||||
|
||||
(defn game-setstate! [this statenew]
|
||||
(reset! (game-state-atom this) statenew))
|
||||
(defn game-setscreen! [this screennew]
|
||||
(reset! (game-screen-atom this) screennew))
|
||||
|
||||
(defmethod update-game :default [state input delta])
|
||||
(defmethod render-game :default [state graphics])
|
||||
(defmethod eval-with-bindings :default [state game f] (f))
|
||||
(defmethod update-game :default [screen input delta])
|
||||
(defmethod render-game :default [screen graphics])
|
||||
(defmethod eval-with-bindings :default [screen game f] (f))
|
||||
|
||||
(defn start-game [title startstate]
|
||||
(let [game (hottub.slick.Game. title startstate)
|
||||
(defn start-game [title spec]
|
||||
(let [game (hottub.slick.Game. title (scr/goto-screen nil spec))
|
||||
container (AppGameContainer. game 640 480 false)]
|
||||
(.setAlwaysRender container true)
|
||||
(.start container)
|
||||
|
|
Loading…
Reference in a new issue