|
240 | 240 |
|
241 | 241 | (defn init-level! |
242 | 242 | "Initializes a new level" |
243 | | - [& {:keys [level]}] |
| 243 | + [{:keys [level ship] :as game-state}] |
244 | 244 | (let [num-asteroids (+ 3 level)] |
245 | | - (swap! game-state assoc |
246 | | - :asteroids (vec (for [_ (range num-asteroids)] |
247 | | - (let [edge (rand-int 4) |
248 | | - x (case edge |
249 | | - 0 (rand-int canvas-width) |
250 | | - 1 canvas-width |
251 | | - 2 (rand-int canvas-width) |
252 | | - (rand-int canvas-height)) |
253 | | - y (case edge |
254 | | - 0 0 |
255 | | - 1 (rand-int canvas-height) |
256 | | - 2 canvas-height |
257 | | - 0)] |
258 | | - (create-asteroid :x x :y y :size-type :large)))) |
259 | | - :bullets [] |
260 | | - :particles [] |
261 | | - :ufo-timer (+ 600 (rand-int 600)) |
262 | | - :ship (assoc (:ship @game-state) |
263 | | - :invulnerable 120)))) |
| 245 | + (merge game-state |
| 246 | + {:asteroids (vec (for [_ (range num-asteroids)] |
| 247 | + (let [edge (rand-int 4) |
| 248 | + x (case edge |
| 249 | + 0 (rand-int canvas-width) |
| 250 | + 1 canvas-width |
| 251 | + 2 (rand-int canvas-width) |
| 252 | + (rand-int canvas-height)) |
| 253 | + y (case edge |
| 254 | + 0 0 |
| 255 | + 1 (rand-int canvas-height) |
| 256 | + 2 canvas-height |
| 257 | + 0)] |
| 258 | + (create-asteroid :x x :y y :size-type :large)))) |
| 259 | + :bullets [] |
| 260 | + :particles [] |
| 261 | + :ufo-timer (+ 600 (rand-int 600)) |
| 262 | + :ship (assoc ship :invulnerable 120)}))) |
264 | 263 |
|
265 | 264 | (defn reset-ship! |
266 | 265 | "Resets ship to center" |
|
278 | 277 |
|
279 | 278 | (defn fire-bullet! |
280 | 279 | "Fires a bullet from the ship" |
281 | | - [] |
| 280 | + [{:keys [ship] :as game-state}] |
282 | 281 | (play-laser-sound) ; Play laser sound |
283 | | - (let [{:keys [x y angle]} (:ship @game-state) |
| 282 | + (let [{:keys [x y angle]} ship |
284 | 283 | bullet-vx (* bullet-speed (Math/cos (- angle (/ Math/PI 2)))) |
285 | 284 | bullet-vy (* bullet-speed (Math/sin (- angle (/ Math/PI 2))))] |
286 | | - (swap! game-state update :bullets conj |
287 | | - {:x x |
288 | | - :y y |
289 | | - :vx bullet-vx |
290 | | - :vy bullet-vy |
291 | | - :life bullet-lifetime}))) |
| 285 | + (update game-state :bullets conj |
| 286 | + {:x x |
| 287 | + :y y |
| 288 | + :vx bullet-vx |
| 289 | + :vy bullet-vy |
| 290 | + :life bullet-lifetime}))) |
292 | 291 |
|
293 | 292 | (defn ufo-fire! |
294 | 293 | "UFO fires bullet at ship" |
295 | | - [& {:keys [ufo]}] |
296 | | - (let [{:keys [ship]} @game-state |
297 | | - dx (- (:x ship) (:x ufo)) |
| 294 | + [{:keys [ship] :as game-state} & {:keys [ufo]}] |
| 295 | + (let [dx (- (:x ship) (:x ufo)) |
298 | 296 | dy (- (:y ship) (:y ufo)) |
299 | 297 | angle (Math/atan2 dy dx) |
300 | 298 | bullet-vx (* 5 (Math/cos angle)) |
301 | 299 | bullet-vy (* 5 (Math/sin angle))] |
302 | | - (swap! game-state update :bullets conj |
303 | | - {:x (:x ufo) |
304 | | - :y (:y ufo) |
305 | | - :vx bullet-vx |
306 | | - :vy bullet-vy |
307 | | - :life bullet-lifetime |
308 | | - :from-ufo true}))) |
| 300 | + (update game-state :bullets conj |
| 301 | + {:x (:x ufo) |
| 302 | + :y (:y ufo) |
| 303 | + :vx bullet-vx |
| 304 | + :vy bullet-vy |
| 305 | + :life bullet-lifetime |
| 306 | + :from-ufo true}))) |
309 | 307 |
|
310 | 308 | ;; ============================================================================ |
311 | 309 | ;; Special Moves |
312 | 310 | ;; ============================================================================ |
313 | 311 |
|
| 312 | +(defn hyperspace [state new-x new-y died?] |
| 313 | + (-> state |
| 314 | + ;; Teleport ship |
| 315 | + (assoc-in [:ship :x] new-x) |
| 316 | + (assoc-in [:ship :y] new-y) |
| 317 | + (assoc-in [:ship :vx] 0) |
| 318 | + (assoc-in [:ship :vy] 0) |
| 319 | + (assoc :hyperspace-cooldown hyperspace-cooldown) |
| 320 | + ;; Conditionally handle death |
| 321 | + (#(if died? |
| 322 | + (-> % |
| 323 | + (update-in [:lives] dec) |
| 324 | + (update :particles |
| 325 | + (fn [particles] |
| 326 | + (vec (concat particles |
| 327 | + (create-particles |
| 328 | + :x new-x |
| 329 | + :y new-y |
| 330 | + :count 12 |
| 331 | + :color "#FFFFFF")))))) |
| 332 | + %)))) |
| 333 | + |
314 | 334 | (defn hyperspace! |
315 | 335 | "Hyperspace jump with risk" |
316 | | - [] |
317 | | - (when (<= (:hyperspace-cooldown @game-state) 0) |
318 | | - (play-hyperspace-sound) ; Play hyperspace sound |
| 336 | + [game-state] |
| 337 | + (when (<= (:hyperspace-cooldown game-state) 0) |
| 338 | + (play-hyperspace-sound) ; Play hyperspace sound |
319 | 339 | (let [new-x (rand-int canvas-width) |
320 | 340 | new-y (rand-int canvas-height) |
321 | 341 | died? (< (rand) 0.1)] |
322 | | - (swap! game-state |
323 | | - (fn [state] |
324 | | - (-> state |
325 | | - ;; Teleport ship |
326 | | - (assoc-in [:ship :x] new-x) |
327 | | - (assoc-in [:ship :y] new-y) |
328 | | - (assoc-in [:ship :vx] 0) |
329 | | - (assoc-in [:ship :vy] 0) |
330 | | - (assoc :hyperspace-cooldown hyperspace-cooldown) |
331 | | - ;; Conditionally handle death |
332 | | - (#(if died? |
333 | | - (-> % |
334 | | - (update-in [:lives] dec) |
335 | | - (update :particles |
336 | | - (fn [particles] |
337 | | - (vec (concat particles |
338 | | - (create-particles |
339 | | - :x new-x |
340 | | - :y new-y |
341 | | - :count 12 |
342 | | - :color "#FFFFFF")))))) |
343 | | - %)))))))) |
| 342 | + (hyperspace game-state new-x new-y died?)))) |
344 | 343 |
|
345 | 344 | ;; ============================================================================ |
346 | 345 | ;; Collision Detection |
|
427 | 426 | (do |
428 | 427 | (when (and (= (mod (:frame-count @game-state) 60) 0) |
429 | 428 | (< (rand) 0.3)) |
430 | | - (ufo-fire! :ufo u)) |
| 429 | + (swap! game-state ufo-fire! :ufo u)) |
431 | 430 | (-> u |
432 | 431 | (update :x + (:vx u)) |
433 | 432 | (update :y + (:vy u)))))))) |
|
558 | 557 | (when (empty? asteroids) |
559 | 558 | (play-level-complete-sound) ; Play victory sound |
560 | 559 | (swap! game-state update :level inc) |
561 | | - (init-level! :level (:level @game-state)))))) |
| 560 | + (swap! game-state init-level!))))) |
562 | 561 |
|
563 | 562 | ;; ============================================================================ |
564 | 563 | ;; Drawing Functions |
|
764 | 763 | :distance 0} |
765 | 764 | :fire-button false |
766 | 765 | :hyperspace-button false}) |
767 | | - (init-level! :level 1)) |
| 766 | + (init-level! {:level 1 :ship {}})) |
768 | 767 |
|
769 | 768 | ;; ============================================================================ |
770 | 769 | ;; Canvas Component |
|
786 | 785 | (swap! keys-pressed conj key) |
787 | 786 | (when (= (:game-status @game-state) :playing) |
788 | 787 | (case key |
789 | | - " " (do (fire-bullet!) (.preventDefault e)) |
790 | | - "x" (hyperspace!) |
791 | | - "X" (hyperspace!) |
| 788 | + " " (do (swap! game-state fire-bullet!) (.preventDefault e)) |
| 789 | + "x" (swap! game-state hyperspace!) |
| 790 | + "X" (swap! game-state hyperspace!) |
792 | 791 | nil))))) |
793 | 792 |
|
794 | 793 | (.addEventListener js/window "keyup" |
|
1033 | 1032 | :color "76, 175, 80" |
1034 | 1033 | :on-press #(do |
1035 | 1034 | (swap! game-state assoc-in [:touch-controls :fire-button] true) |
1036 | | - (fire-bullet!)) |
| 1035 | + (swap! game-state fire-bullet!)) |
1037 | 1036 | :on-release #(swap! game-state assoc-in [:touch-controls :fire-button] false)] |
1038 | 1037 | [touch-action-button |
1039 | 1038 | :label "HYPER" |
|
1042 | 1041 | :color "255, 152, 0" |
1043 | 1042 | :on-press #(do |
1044 | 1043 | (swap! game-state assoc-in [:touch-controls :hyperspace-button] true) |
1045 | | - (hyperspace!)) |
| 1044 | + (swap! game-state hyperspace!)) |
1046 | 1045 | :on-release #(swap! game-state assoc-in [:touch-controls :hyperspace-button] false)]]])) |
1047 | 1046 |
|
1048 | 1047 | ;; ============================================================================ |
|
0 commit comments