Skip to content

Commit a1b461d

Browse files
authored
Merge pull request #137 from burinc/games-sound-and-swap-fixes
Improve the code per review from Erik
2 parents 946f8c4 + e0cdf33 commit a1b461d

File tree

3 files changed

+453
-94
lines changed

3 files changed

+453
-94
lines changed

src/scittle/games/asteroids.cljs

Lines changed: 104 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,70 @@
1515
;; Game Constants
1616
;; ============================================================================
1717

18+
;; ============================================================================
19+
;; Audio System (Web Audio API)
20+
;; ============================================================================
21+
22+
(def audio-context
23+
"Web Audio API context for sound generation"
24+
(when (exists? js/AudioContext)
25+
(js/AudioContext.)))
26+
27+
(defn play-tone
28+
"Plays a tone at the specified frequency for the given duration"
29+
[& {:keys [frequency duration volume]
30+
:or {frequency 440 duration 0.2 volume 0.3}}]
31+
(when audio-context
32+
(try
33+
(let [oscillator (.createOscillator audio-context)
34+
gain-node (.createGain audio-context)]
35+
(.connect oscillator gain-node)
36+
(.connect gain-node (.-destination audio-context))
37+
(set! (.-value (.-frequency oscillator)) frequency)
38+
(set! (.-value (.-gain gain-node)) volume)
39+
(.start oscillator)
40+
(.stop oscillator (+ (.-currentTime audio-context) duration)))
41+
(catch js/Error e
42+
(js/console.error "Audio error:" e)))))
43+
44+
(defn play-laser-sound
45+
"Plays a laser shooting sound"
46+
[]
47+
(play-tone :frequency 800 :duration 0.1 :volume 0.2))
48+
49+
(defn play-explosion-sound
50+
"Plays an explosion sound for asteroids and UFOs"
51+
[]
52+
(play-tone :frequency 100 :duration 0.2 :volume 0.3))
53+
54+
(defn play-thrust-sound
55+
"Plays a ship thrust/engine sound"
56+
[]
57+
(play-tone :frequency 150 :duration 0.08 :volume 0.15))
58+
59+
(defn play-hyperspace-sound
60+
"Plays a hyperspace jump sound with descending frequencies"
61+
[]
62+
;; Descending frequencies for warp effect
63+
(doseq [[idx freq] (map-indexed vector [880 660 440 220])]
64+
(js/setTimeout
65+
#(play-tone :frequency freq :duration 0.1 :volume 0.25)
66+
(* idx 50))))
67+
68+
(defn play-hit-sound
69+
"Plays a sound when the ship is hit"
70+
[]
71+
(play-tone :frequency 150 :duration 0.3 :volume 0.4))
72+
73+
(defn play-level-complete-sound
74+
"Plays a victory sound for completing a level"
75+
[]
76+
;; Ascending notes for victory
77+
(doseq [[idx freq] (map-indexed vector [523 659 784 1047])]
78+
(js/setTimeout
79+
#(play-tone :frequency freq :duration 0.2 :volume 0.25)
80+
(* idx 100))))
81+
1882
(def canvas-width 800)
1983
(def canvas-height 600)
2084
(def ship-size 10)
@@ -215,6 +279,7 @@
215279
(defn fire-bullet!
216280
"Fires a bullet from the ship"
217281
[]
282+
(play-laser-sound) ; Play laser sound
218283
(let [{:keys [x y angle]} (:ship @game-state)
219284
bullet-vx (* bullet-speed (Math/cos (- angle (/ Math/PI 2))))
220285
bullet-vy (* bullet-speed (Math/sin (- angle (/ Math/PI 2))))]
@@ -250,19 +315,32 @@
250315
"Hyperspace jump with risk"
251316
[]
252317
(when (<= (:hyperspace-cooldown @game-state) 0)
253-
(swap! game-state assoc-in [:ship :x] (rand-int canvas-width))
254-
(swap! game-state assoc-in [:ship :y] (rand-int canvas-height))
255-
(swap! game-state assoc-in [:ship :vx] 0)
256-
(swap! game-state assoc-in [:ship :vy] 0)
257-
(swap! game-state assoc :hyperspace-cooldown hyperspace-cooldown)
258-
;; 10% chance of explosion (risky!)
259-
(when (< (rand) 0.1)
260-
(swap! game-state update-in [:lives] dec)
261-
(swap! game-state update :particles
262-
#(vec (concat % (create-particles :x (:x (:ship @game-state))
263-
:y (:y (:ship @game-state))
264-
:count 12
265-
:color "#FFFFFF")))))))
318+
(play-hyperspace-sound) ; Play hyperspace sound
319+
(let [new-x (rand-int canvas-width)
320+
new-y (rand-int canvas-height)
321+
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+
%))))))))
266344

267345
;; ============================================================================
268346
;; Collision Detection
@@ -297,7 +375,7 @@
297375
(swap! game-state update :ufos conj (create-ufo))
298376
(swap! game-state assoc :ufo-timer (+ 900 (rand-int 900))))
299377

300-
;; Update ship
378+
;; Update ship
301379
(swap! game-state update :ship
302380
(fn [s]
303381
(let [new-vx (if (:thrusting s)
@@ -316,6 +394,11 @@
316394
(update :y #(wrap-position :value (+ % new-vy) :max-val canvas-height))
317395
(update :invulnerable #(max 0 (dec %)))))))
318396

397+
;; Play thrust sound (throttled to every 8 frames)
398+
(when (and (:thrusting ship)
399+
(= (mod (:frame-count @game-state) 8) 0))
400+
(play-thrust-sound))
401+
319402
;; Update bullets
320403
(swap! game-state update :bullets
321404
(fn [bs]
@@ -386,8 +469,9 @@
386469
:count 5
387470
:color "#FFFFFF"))))
388471

389-
;; Apply all collision effects at once
472+
;; Apply all collision effects at once
390473
(when (seq @hit-bullets)
474+
(play-explosion-sound) ; Play explosion sound for asteroid destruction
391475
(swap! game-state update :bullets #(vec (remove (fn [b] (contains? @hit-bullets b)) %)))
392476
(swap! game-state update :asteroids #(vec (remove (fn [a] (contains? @hit-asteroids a)) %)))
393477
;; Only add new asteroids if we're under the limit
@@ -423,6 +507,7 @@
423507
:color "#FF00FF"))))
424508

425509
(when (seq @hit-bullets)
510+
(play-explosion-sound) ; Play explosion sound for UFO destruction
426511
(swap! game-state update :bullets #(vec (remove (fn [b] (contains? @hit-bullets b)) %)))
427512
(swap! game-state update :ufos #(vec (remove (fn [u] (contains? @hit-ufos u)) %)))
428513
(swap! game-state update :score + @score-added)
@@ -433,6 +518,7 @@
433518
(doseq [asteroid asteroids]
434519
(when (check-collision :obj1 ship :obj2 asteroid
435520
:radius1 ship-size :radius2 (:size asteroid))
521+
(play-hit-sound) ; Play hit sound when ship is hit
436522
(swap! game-state update :lives dec)
437523
(swap! game-state update :particles
438524
#(vec (concat % (create-particles :x (:x ship)
@@ -450,6 +536,7 @@
450536
:when (:from-ufo bullet)]
451537
(when (check-collision :obj1 ship :obj2 bullet
452538
:radius1 ship-size :radius2 3)
539+
(play-hit-sound) ; Play hit sound when ship is hit by UFO bullet
453540
(swap! game-state update :bullets #(vec (remove (fn [b] (= b bullet)) %)))
454541
(swap! game-state update :lives dec)
455542
(swap! game-state update :particles
@@ -462,8 +549,9 @@
462549
(swap! game-state assoc :game-status :game-over)
463550
(swap! game-state update :high-score max (:score @game-state))))))
464551

465-
;; Check level complete
552+
;; Check level complete
466553
(when (empty? asteroids)
554+
(play-level-complete-sound) ; Play victory sound
467555
(swap! game-state update :level inc)
468556
(init-level! :level (:level @game-state))))))
469557

0 commit comments

Comments
 (0)