commit
f234077cd7
12 changed files with 695 additions and 0 deletions
@ -0,0 +1,14 @@
@@ -0,0 +1,14 @@
|
||||
.git |
||||
/resources/public/js/compiled/** |
||||
figwheel_server.log |
||||
pom.xml |
||||
*jar |
||||
/lib/ |
||||
/classes/ |
||||
/out/ |
||||
/target/ |
||||
.lein-deps-sum |
||||
.lein-repl-history |
||||
.lein-plugins/ |
||||
.repl |
||||
.nrepl-port |
@ -0,0 +1,13 @@
@@ -0,0 +1,13 @@
|
||||
/resources/public/js/compiled/** |
||||
figwheel_server.log |
||||
pom.xml |
||||
*jar |
||||
/lib/ |
||||
/classes/ |
||||
/out/ |
||||
/target/ |
||||
.lein-deps-sum |
||||
.lein-repl-history |
||||
.lein-plugins/ |
||||
.repl |
||||
.nrepl-port |
@ -0,0 +1,10 @@
@@ -0,0 +1,10 @@
|
||||
#stage 1 |
||||
FROM debian as debian |
||||
WORKDIR /app |
||||
COPY . . |
||||
RUN apt update && apt install -y leiningen |
||||
RUN lein cljsbuild once |
||||
|
||||
#stage 2 |
||||
FROM nginx |
||||
COPY --from=debian /app/resources/public /usr/share/nginx/html |
@ -0,0 +1,11 @@
@@ -0,0 +1,11 @@
|
||||
pipeline { |
||||
agent any |
||||
options { |
||||
skipStagesAfterUnstable() |
||||
} |
||||
stages { |
||||
stage('Build') { |
||||
sh 'echo "Hello!"' |
||||
} |
||||
} |
||||
} |
@ -0,0 +1,40 @@
@@ -0,0 +1,40 @@
|
||||
# pong |
||||
|
||||
FIXME: Write a one-line description of your library/project. |
||||
|
||||
## Overview |
||||
|
||||
FIXME: Write a paragraph about the library/project and highlight its goals. |
||||
|
||||
## Setup |
||||
|
||||
To get an interactive development environment run: |
||||
|
||||
|
||||
lein figwheel |
||||
|
||||
and open your browser at [localhost:3449](http://localhost:3449/). |
||||
This will auto compile and send all changes to the browser without the |
||||
need to reload. After the compilation process is complete, you will |
||||
get a Browser Connected REPL. An easy way to try it is: |
||||
|
||||
(js/alert "Am I connected?") |
||||
|
||||
and you should see an alert in the browser window. |
||||
|
||||
To clean all compiled files: |
||||
|
||||
lein clean |
||||
|
||||
To create a production build run: |
||||
|
||||
lein do clean, cljsbuild once min |
||||
|
||||
And open your browser in `resources/public/index.html`. You will not |
||||
get live reloading, nor a REPL. |
||||
|
||||
## License |
||||
|
||||
Copyright © 2014 FIXME |
||||
|
||||
Distributed under the Eclipse Public License either version 1.0 or (at your option) any later version. |
@ -0,0 +1,42 @@
@@ -0,0 +1,42 @@
|
||||
(ns user |
||||
(:require |
||||
[figwheel-sidecar.repl-api :as f])) |
||||
|
||||
;; user is a namespace that the Clojure runtime looks for and |
||||
;; loads if its available |
||||
|
||||
;; You can place helper functions in here. This is great for starting |
||||
;; and stopping your webserver and other development services |
||||
|
||||
;; The definitions in here will be available if you run "lein repl" or launch a |
||||
;; Clojure repl some other way |
||||
|
||||
;; You have to ensure that the libraries you :require are listed in your dependencies |
||||
|
||||
;; Once you start down this path |
||||
;; you will probably want to look at |
||||
;; tools.namespace https://github.com/clojure/tools.namespace |
||||
;; and Component https://github.com/stuartsierra/component |
||||
|
||||
|
||||
(defn fig-start |
||||
"This starts the figwheel server and watch based auto-compiler." |
||||
[] |
||||
;; this call will only work as long as your :cljsbuild and |
||||
;; :figwheel configurations are at the top level of your project.clj |
||||
;; and are not spread across different lein profiles |
||||
|
||||
;; otherwise you can pass a configuration into start-figwheel! manually |
||||
(f/start-figwheel!)) |
||||
|
||||
(defn fig-stop |
||||
"Stop the figwheel server and watch based auto-compiler." |
||||
[] |
||||
(f/stop-figwheel!)) |
||||
|
||||
;; if you are in an nREPL environment you will need to make sure you |
||||
;; have setup piggieback for this to work |
||||
(defn cljs-repl |
||||
"Launch a ClojureScript REPL that is connected to your build and host environment." |
||||
[] |
||||
(f/cljs-repl)) |
@ -0,0 +1,95 @@
@@ -0,0 +1,95 @@
|
||||
(defproject pong "0.1.0-SNAPSHOT" |
||||
:description "FIXME: write this!" |
||||
:url "http://example.com/FIXME" |
||||
:license {:name "Eclipse Public License" |
||||
:url "http://www.eclipse.org/legal/epl-v10.html"} |
||||
|
||||
:min-lein-version "2.9.1" |
||||
|
||||
:dependencies [[org.clojure/clojure "1.10.0"] |
||||
[org.clojure/clojurescript "1.10.773"] |
||||
[org.clojure/core.async "0.4.500"]] |
||||
|
||||
:plugins [[lein-figwheel "0.5.20"] |
||||
[lein-cljsbuild "1.1.7" :exclusions [[org.clojure/clojure]]]] |
||||
|
||||
:source-paths ["src"] |
||||
|
||||
:cljsbuild {:builds |
||||
[{:id "dev" |
||||
:source-paths ["src"] |
||||
|
||||
;; The presence of a :figwheel configuration here |
||||
;; will cause figwheel to inject the figwheel client |
||||
;; into your build |
||||
:figwheel {:on-jsload "pong.core/on-js-reload" |
||||
;; :open-urls will pop open your application |
||||
;; in the default browser once Figwheel has |
||||
;; started and compiled your application. |
||||
;; Comment this out once it no longer serves you. |
||||
:open-urls ["http://localhost:3449/index.html"]} |
||||
|
||||
:compiler {:main pong.core |
||||
:asset-path "js/compiled/out" |
||||
:output-to "resources/public/js/compiled/pong.js" |
||||
:output-dir "resources/public/js/compiled/out" |
||||
:source-map-timestamp true |
||||
;; To console.log CLJS data-structures make sure you enable devtools in Chrome |
||||
;; https://github.com/binaryage/cljs-devtools |
||||
:preloads [devtools.preload]}} |
||||
;; This next build is a compressed minified build for |
||||
;; production. You can build this with: |
||||
;; lein cljsbuild once min |
||||
{:id "min" |
||||
:source-paths ["src"] |
||||
:compiler {:output-to "resources/public/js/compiled/pong.js" |
||||
:main pong.core |
||||
:optimizations :advanced |
||||
:pretty-print false}}]} |
||||
|
||||
:figwheel {;; :http-server-root "public" ;; default and assumes "resources" |
||||
;; :server-port 3449 ;; default |
||||
;; :server-ip "127.0.0.1" |
||||
|
||||
:css-dirs ["resources/public/css"] ;; watch and update CSS |
||||
|
||||
;; Start an nREPL server into the running figwheel process |
||||
;; :nrepl-port 7888 |
||||
|
||||
;; Server Ring Handler (optional) |
||||
;; if you want to embed a ring handler into the figwheel http-kit |
||||
;; server, this is for simple ring servers, if this |
||||
|
||||
;; doesn't work for you just run your own server :) (see lein-ring) |
||||
|
||||
;; :ring-handler hello_world.server/handler |
||||
|
||||
;; To be able to open files in your editor from the heads up display |
||||
;; you will need to put a script on your path. |
||||
;; that script will have to take a file path and a line number |
||||
;; ie. in ~/bin/myfile-opener |
||||
;; #! /bin/sh |
||||
;; emacsclient -n +$2 $1 |
||||
;; |
||||
;; :open-file-command "myfile-opener" |
||||
|
||||
;; if you are using emacsclient you can just use |
||||
;; :open-file-command "emacsclient" |
||||
|
||||
;; if you want to disable the REPL |
||||
;; :repl false |
||||
|
||||
;; to configure a different figwheel logfile path |
||||
;; :server-logfile "tmp/logs/figwheel-logfile.log" |
||||
|
||||
;; to pipe all the output to the repl |
||||
;; :server-logfile false |
||||
} |
||||
|
||||
:profiles {:dev {:dependencies [[binaryage/devtools "1.0.0"] |
||||
[figwheel-sidecar "0.5.20"]] |
||||
;; need to add dev source path here to get user.clj loaded |
||||
:source-paths ["src" "dev"] |
||||
;; need to add the compiled assets to the :clean-targets |
||||
:clean-targets ^{:protect false} ["resources/public/js/compiled" |
||||
:target-path]}}) |
@ -0,0 +1,8 @@
@@ -0,0 +1,8 @@
|
||||
/* some style */ |
||||
|
||||
body { |
||||
margin: 0; |
||||
display: flex; |
||||
background-color: black; |
||||
} |
||||
|
After Width: | Height: | Size: 2.9 KiB |
@ -0,0 +1,13 @@
@@ -0,0 +1,13 @@
|
||||
<!DOCTYPE html> |
||||
<html> |
||||
<head> |
||||
<meta charset="UTF-8"> |
||||
<meta name="viewport" content="width=device-width, initial-scale=1"> |
||||
<link href="css/style.css" rel="stylesheet" type="text/css"> |
||||
<link rel="icon" href="favicon.ico"> |
||||
</head> |
||||
<body> |
||||
<canvas id="game" width="200" height="100"></canvas> |
||||
<script src="js/compiled/pong.js" type="text/javascript"></script> |
||||
</body> |
||||
</html> |
@ -0,0 +1,371 @@
@@ -0,0 +1,371 @@
|
||||
(ns pong.core |
||||
(:require [pong.menu :as menu])) |
||||
|
||||
(enable-console-print!) |
||||
|
||||
;; define your app data so that it doesn't get over-written on reload |
||||
|
||||
(declare game-draw next-frame relaunch-ball game-loop) |
||||
|
||||
(defonce keyboard (atom {})) |
||||
|
||||
(def canvas (.getElementById js/document "game")) |
||||
(def context (.getContext canvas "2d")) |
||||
(def paddle-height 320) |
||||
(def paddle-width 40) |
||||
(def paddle-speed 1) |
||||
(def ball-radius 20) |
||||
(def ball-speed 0.75) |
||||
|
||||
|
||||
(defn window-height |
||||
[] |
||||
(.-innerHeight js/window)) |
||||
|
||||
(defn window-width |
||||
[] |
||||
(.-innerWidth js/window)) |
||||
|
||||
(defn unit-vector |
||||
[v] |
||||
(let [length (Math.sqrt (reduce + (map #(Math.pow % 2) v)))] |
||||
(map #(/ % length) v))) |
||||
|
||||
(defn contains-point? |
||||
[rect x y] |
||||
(and (> x (:x rect)) |
||||
(< x (+ (:x rect) (:w rect))) |
||||
(> y (:y rect)) |
||||
(< y (+ (:y rect) (:h rect))))) |
||||
|
||||
(defn collision? |
||||
[ball paddle] |
||||
(or (contains-point? paddle (- (:x ball) (:radius ball)) (:y ball)) |
||||
(contains-point? paddle (+ (:x ball) (:radius ball)) (:y ball)))) |
||||
|
||||
(defn apply-ball-velocity |
||||
[{:keys [ball progress] :as state}] |
||||
(let [velocity (map #(* % progress) (:velocity ball))] |
||||
(assoc state :ball |
||||
(assoc ball |
||||
:x (+ (first velocity) (:x ball)) |
||||
:y (+ (second velocity) (:y ball)))))) |
||||
|
||||
(defn bounce-ball-off-top-wall |
||||
[{{y :y radius :radius [vx vy] :velocity} :ball :as state}] |
||||
(if (< (- y radius) 0) |
||||
(as-> state state |
||||
(assoc-in state [:ball :y] radius) |
||||
(assoc-in state [:ball :velocity] [vx (- vy)])) |
||||
state)) |
||||
|
||||
(defn bounce-ball-off-bottom-wall |
||||
[{{y :y radius :radius [vx vy] :velocity} :ball :as state}] |
||||
(if (> (+ y radius) (window-height)) |
||||
(as-> state state |
||||
(assoc-in state [:ball :y] (- (window-height) radius)) |
||||
(assoc-in state [:ball :velocity] [vx (- vy)])) |
||||
state)) |
||||
|
||||
(defn bounce-ball-off-paddle |
||||
[{ball-y :y [dx] :velocity} {paddle-y :y paddle-h :h}] |
||||
(unit-vector [(* (- (/ dx (Math.abs dx))) (window-width)) |
||||
(* (- (/ (- ball-y paddle-y) paddle-h) 0.5) (window-height))])) |
||||
|
||||
(defn bounce-ball-off-left-paddle |
||||
[{paddle :left-paddle ball :ball :as state}] |
||||
(assoc-in state [:ball :velocity] (bounce-ball-off-paddle ball paddle))) |
||||
|
||||
(defn bounce-ball-off-right-paddle |
||||
[{paddle :right-paddle ball :ball :as state}] |
||||
(assoc-in state [:ball :velocity] (bounce-ball-off-paddle ball paddle))) |
||||
|
||||
(defn put-ball-infront-of-left-paddle |
||||
[{{:keys [radius]} :ball {:keys [x w]} :left-paddle :as state}] |
||||
(assoc-in state [:ball :x] (+ x w radius))) |
||||
|
||||
(defn put-ball-infront-of-right-paddle |
||||
[{{:keys [radius]} :ball {:keys [x w]} :right-paddle :as state}] |
||||
(assoc-in state [:ball :x] (- x radius))) |
||||
|
||||
(defn bounce-ball-off-left-paddle? |
||||
[{:keys [ball left-paddle] :as state}] |
||||
(if (collision? ball left-paddle) |
||||
(-> state |
||||
put-ball-infront-of-left-paddle |
||||
bounce-ball-off-left-paddle) |
||||
state)) |
||||
|
||||
(defn bounce-ball-off-right-paddle? |
||||
[{:keys [ball right-paddle] :as state}] |
||||
(if (collision? ball right-paddle) |
||||
(-> state |
||||
put-ball-infront-of-right-paddle |
||||
bounce-ball-off-right-paddle) |
||||
state)) |
||||
|
||||
(defn launch-ball-left |
||||
[state] |
||||
(assoc state :ball (relaunch-ball -1))) |
||||
|
||||
(defn launch-ball-right |
||||
[state] |
||||
(assoc state :ball (relaunch-ball 1))) |
||||
|
||||
(defn increase-right-player-score |
||||
[state] |
||||
(update-in state [:score :right] inc)) |
||||
|
||||
(defn increase-left-player-score |
||||
[state] |
||||
(update-in state [:score :left] inc)) |
||||
|
||||
(defn left-player-scored |
||||
[{{:keys [x radius]} :ball :as state}] |
||||
(if (> (- x radius) (window-width)) |
||||
(-> state |
||||
launch-ball-right |
||||
increase-left-player-score) |
||||
state)) |
||||
|
||||
(defn right-player-scored |
||||
[{{:keys [x radius]} :ball :as state}] |
||||
(if (< (+ x radius) 0) |
||||
(-> state |
||||
launch-ball-left |
||||
increase-right-player-score) |
||||
state)) |
||||
|
||||
(defn apply-paddle-speed |
||||
[paddle direction progress] |
||||
(direction (:y paddle) (* paddle-speed progress))) |
||||
|
||||
(defn handle-left-player-paddle-up-movement |
||||
[{:keys [progress left-paddle] :as state}] |
||||
(if (:KeyW @keyboard) |
||||
(assoc-in state |
||||
[:left-paddle :y] |
||||
(apply-paddle-speed left-paddle - progress)) |
||||
state)) |
||||
|
||||
(defn handle-left-player-paddle-down-movement |
||||
[{:keys [progress left-paddle] :as state}] |
||||
(if (:KeyS @keyboard) |
||||
(assoc-in state |
||||
[:left-paddle :y] |
||||
(apply-paddle-speed left-paddle + progress)) |
||||
state)) |
||||
|
||||
(defn keep-left-paddle-in-bounds |
||||
[{:keys [left-paddle] :as state}] |
||||
(assoc-in state |
||||
[:left-paddle :y] |
||||
(max 0 (min (- (window-height) (:h left-paddle)) |
||||
(:y left-paddle))))) |
||||
|
||||
(defn update-left-paddle |
||||
[{:keys [progress left-paddle] :as state}] |
||||
(-> state |
||||
handle-left-player-paddle-down-movement |
||||
handle-left-player-paddle-up-movement |
||||
keep-left-paddle-in-bounds)) |
||||
|
||||
(defn stick-right-paddle-to-right-of-screen |
||||
[{:keys [right-paddle] :as state}] |
||||
(assoc-in state [:right-paddle :x] (- (window-width) (:w right-paddle)))) |
||||
|
||||
(defn handle-right-player-paddle-up-movement |
||||
[{:keys [progress right-paddle] :as state}] |
||||
(if (:ArrowUp @keyboard) |
||||
(assoc-in state |
||||
[:right-paddle :y] |
||||
(apply-paddle-speed right-paddle - progress)) |
||||
state)) |
||||
|
||||
(defn handle-right-player-paddle-down-movement |
||||
[{:keys [progress right-paddle] :as state}] |
||||
(if (:ArrowDown @keyboard) |
||||
(assoc-in state |
||||
[:right-paddle :y] |
||||
(apply-paddle-speed right-paddle + progress)) |
||||
state)) |
||||
|
||||
(defn player-update-right-paddle |
||||
[state] |
||||
(-> state |
||||
handle-right-player-paddle-up-movement |
||||
handle-right-player-paddle-down-movement)) |
||||
|
||||
(defn computer-update-right-paddle |
||||
[{:keys [progress right-paddle ball] :as state}] |
||||
(let [paddle-middle (+ (:y right-paddle) (/ (:h right-paddle) 2)) |
||||
speed (* paddle-speed progress)] |
||||
(assoc-in state [:right-paddle :y] |
||||
(cond |
||||
(< (:y ball) (- paddle-middle speed)) |
||||
(max (- (:y right-paddle) speed) 0) |
||||
(> (:y ball) (+ paddle-middle speed)) |
||||
(min (+ (:y right-paddle) speed) |
||||
(- (window-height) (:h right-paddle))) |
||||
:else (:y right-paddle))))) |
||||
|
||||
(defn draw-rect |
||||
[{x :x y :y w :w h :h}] |
||||
(.fillRect context x y w h)) |
||||
|
||||
(defn fill-circle |
||||
[{x :x y :y radius :radius}] |
||||
(.beginPath context) |
||||
(.arc context x y radius 0 (* 2 Math.PI)) |
||||
(.fill context)) |
||||
|
||||
(defn draw-score |
||||
[{:keys [left right]}] |
||||
(set! (.-font context) "30px monospace") |
||||
(.fillText context (str left " | " right) (/ (window-width) 2) 30)) |
||||
|
||||
(defn game-draw |
||||
[{:keys [left-paddle right-paddle ball score] :as state}] |
||||
(.clearRect context 0 0 (window-width) (window-height)) |
||||
(set! (.-fillStyle context) "#FFF") |
||||
(draw-rect left-paddle) |
||||
(draw-rect right-paddle) |
||||
(fill-circle ball) |
||||
(draw-score score) |
||||
state) |
||||
|
||||
(defn handle-return-to-menu |
||||
[{:keys [keyboard] :as state}] |
||||
(if (:Escape keyboard) |
||||
(assoc state :on-loop menu/scene) |
||||
state)) |
||||
|
||||
(defn game-loop |
||||
[{:keys [update-right-paddle] :as state}] |
||||
(-> state |
||||
update-left-paddle |
||||
update-right-paddle |
||||
stick-right-paddle-to-right-of-screen |
||||
apply-ball-velocity |
||||
bounce-ball-off-top-wall |
||||
bounce-ball-off-bottom-wall |
||||
bounce-ball-off-left-paddle? |
||||
bounce-ball-off-right-paddle? |
||||
left-player-scored |
||||
right-player-scored |
||||
handle-return-to-menu |
||||
game-draw)) |
||||
|
||||
(defn next-frame |
||||
[last-render state] |
||||
(.requestAnimationFrame js/window |
||||
#(next-frame % ((:on-loop state) |
||||
(merge state |
||||
{:window-width (.-innerWidth js/window) |
||||
:window-height (.-innerHeight js/window) |
||||
:keyboard @keyboard |
||||
:progress (- % last-render)}))))) |
||||
|
||||
(defn vec* |
||||
[v d] |
||||
(map #(* % d) v)) |
||||
|
||||
(defn one-or-minus-one |
||||
[] |
||||
(let [n (- (rand 2) 1)] (/ n (Math.abs n)))) |
||||
|
||||
(defn launch-ball [x y dx dy] |
||||
{:x x |
||||
:y y |
||||
:radius ball-radius |
||||
:velocity (vec* (unit-vector [dx dy]) ball-speed)}) |
||||
|
||||
(defn relaunch-ball |
||||
[direction] |
||||
(launch-ball (/ (window-width) 2) |
||||
(rand (window-height)) |
||||
(* direction (/ (window-width) 2)) |
||||
(- (rand (window-height)) (/ (window-height) 2)))) |
||||
|
||||
(defn start-game |
||||
[state update-right-paddle] |
||||
(merge state |
||||
{:left-paddle {:x 0.0 |
||||
:y (/ (- (window-height) paddle-height) 2) |
||||
:w paddle-width |
||||
:h paddle-height} |
||||
:right-paddle {:x (- (window-width) paddle-width) |
||||
:y (/ (- (window-height) paddle-height) 2) |
||||
:w paddle-width |
||||
:h paddle-height} |
||||
:update-right-paddle update-right-paddle |
||||
:ball (relaunch-ball (one-or-minus-one)) |
||||
:score {:left 0 :right 0} |
||||
:on-loop game-loop})) |
||||
|
||||
(defn start-one-player-game |
||||
[state] |
||||
(println "start one player game") |
||||
(start-game state computer-update-right-paddle)) |
||||
|
||||
(defn start-two-players-game |
||||
[state] |
||||
(println "start two players game") |
||||
(start-game state player-update-right-paddle)) |
||||
|
||||
|
||||
(next-frame (.now js/performance) |
||||
{:on-loop menu/scene |
||||
:menu-item-selected 0 |
||||
:menu-items [{:txt "1 Player" |
||||
:action start-one-player-game} |
||||
{:txt "2 Players" |
||||
:action start-two-players-game}] |
||||
:context context}) |
||||
|
||||
(defn set-canvas-size-to-window-size |
||||
[] |
||||
(set! (.-width canvas) (.-innerWidth js/window)) |
||||
(set! (.-height canvas) (.-innerHeight js/window))) |
||||
|
||||
(set-canvas-size-to-window-size) |
||||
|
||||
(set! (.-onresize js/window) |
||||
set-canvas-size-to-window-size) |
||||
|
||||
;; (def update-game-state (partial swap! game-state assoc)) |
||||
;; (def update-left-paddle-movement (partial update-game-state :left-paddle-movement)) |
||||
;; (def update-right-paddle-movement (partial update-game-state :right-paddle-movement)) |
||||
|
||||
;; (swap! game-state assoc :on-loop menu-loop) |
||||
;; (fn [event] |
||||
;; (let [code (.-code event)] |
||||
;; (case code |
||||
;; "KeyW" (update-left-paddle-movement :up) |
||||
;; "KeyS" (update-left-paddle-movement :down) |
||||
;; "ArrowUp" (update-right-paddle-movement :up) |
||||
;; "ArrowDown" (update-right-paddle-movement :down) |
||||
;; "Escape" (swap! game-state assoc :on-loop menu-loop) |
||||
;; nil))) |
||||
|
||||
;; (fn [event] |
||||
;; (let [code (.-code event)] |
||||
;; (case code |
||||
;; "KeyW" (and (= (:left-paddle-movement @game-state) :up) |
||||
;; (update-left-paddle-movement nil)) |
||||
;; "KeyS" (and (= (:left-paddle-movement @game-state) :down) |
||||
;; (update-left-paddle-movement nil)) |
||||
;; "ArrowUp" (and (= (:right-paddle-movement @game-state) :up) |
||||
;; (update-right-paddle-movement nil)) |
||||
;; "ArrowDown" (and (= (:right-paddle-movement @game-state) :down) |
||||
;; (update-right-paddle-movement nil)) |
||||
;; nil))) |
||||
|
||||
(.addEventListener js/document "keydown" #(swap! keyboard assoc (keyword (.-code %)) %)) |
||||
(.addEventListener js/document "keyup" #(swap! keyboard dissoc (keyword (.-code %)))) |
||||
|
||||
(defn on-js-reload [] |
||||
;; optionally touch your app-state to force rerendering depending on |
||||
;; your application |
||||
;; (swap! app-state update-in [:__figwheel_counter] inc) |
||||
) |
@ -0,0 +1,78 @@
@@ -0,0 +1,78 @@
|
||||
(ns pong.menu) |
||||
|
||||
(def next-key-ms 200) |
||||
|
||||
(defn decrease-next-key |
||||
[{:keys [next-key progress] :as state}] |
||||
(if (> next-key 0) |
||||
(assoc state :next-key (- next-key progress)) |
||||
state)) |
||||
|
||||
(defn handle-menu-up |
||||
[{:keys [menu-item-selected menu-items keyboard next-key] :as state}] |
||||
(if (and (or (:KeyW keyboard) (:ArrowUp keyboard)) (<= next-key 0)) |
||||
(merge state |
||||
{:menu-item-selected (mod (dec (+ menu-item-selected (count menu-items))) |
||||
(count menu-items)) |
||||
:next-key next-key-ms}) |
||||
state)) |
||||
|
||||
(defn handle-menu-down |
||||
[{:keys [menu-item-selected menu-items keyboard next-key] :as state}] |
||||
(if (and (or (:KeyS keyboard) (:ArrowDown keyboard)) (<= next-key 0)) |
||||
(merge state |
||||
{:menu-item-selected (mod (inc menu-item-selected) (count menu-items)) |
||||
:next-key next-key-ms}) |
||||
state)) |
||||
|
||||
(defn handle-menu-selected |
||||
[{:keys [menu-item-selected menu-items keyboard] :as state}] |
||||
(if (:Enter keyboard) |
||||
((:action (nth menu-items menu-item-selected)) |
||||
state) |
||||
state)) |
||||
|
||||
(defn draw-text-centered |
||||
[context txt x y] |
||||
(let [width (.-width (.measureText context txt))] |
||||
(.fillText context txt (- x (/ width 2)) y))) |
||||
|
||||
(defn draw-menu-item |
||||
[context item selected x y] |
||||
(draw-text-centered context |
||||
(str (when selected ">") |
||||
(:txt item) |
||||
(when selected "<")) |
||||
x |
||||
y)) |
||||
|
||||
(defn draw |
||||
[{:keys [context menu-item-selected window-width window-height menu-items] :as state}] |
||||
(.clearRect context 0 0 |
||||
window-width |
||||
window-height) |
||||
|
||||
(set! (.-fillStyle context) "#FFF") |
||||
(set! (.-font context) "30px monospace") |
||||
|
||||
(draw-text-centered context "Pariatech's Pong" (/ window-width 2) 60) |
||||
|
||||
|
||||
(let [middle-horizontally (/ window-width 2) |
||||
offset-vertically (/ (- window-height (* (count menu-items) 40)) 2)] |
||||
(doall (map-indexed #(draw-menu-item context |
||||
%2 |
||||
(= %1 (Math/floor menu-item-selected)) |
||||
middle-horizontally |
||||
(+ offset-vertically (* %1 40))) |
||||
menu-items))) |
||||
state) |
||||
|
||||
(defn scene |
||||
[state] |
||||
(-> state |
||||
decrease-next-key |
||||
handle-menu-up |
||||
handle-menu-down |
||||
handle-menu-selected |
||||
draw)) |
Loading…
Reference in new issue