|
322 | 322 | (assoc star :y new-y)))) |
323 | 323 | stars)))) |
324 | 324 | ;; Update formation movement (side-to-side only, no descent) |
325 | | - (let [direction (:formation-direction @game-state)] |
326 | | - (swap! game-state update :formation-offset |
327 | | - (fn [{:keys [x y]}] |
328 | | - (let [new-x (+ x (* direction 0.5))] |
329 | | - (cond |
330 | | - (and (>= new-x 30) (= direction 1)) |
331 | | - (do (swap! game-state assoc :formation-direction -1) |
332 | | - {:x 30 :y y}) ; Keep y the same - no descent! |
333 | | - (and (<= new-x -30) (= direction -1)) |
334 | | - (do (swap! game-state assoc :formation-direction 1) |
335 | | - {:x -30 :y y}) ; Keep y the same - no descent! |
336 | | - :else {:x new-x :y y}))))) |
| 325 | + (let [direction (:formation-direction @game-state) |
| 326 | + offset (:formation-offset @game-state) |
| 327 | + x (:x offset) |
| 328 | + y (:y offset) |
| 329 | + new-x (+ x (* direction 0.5))] |
| 330 | + ;; Single atomic update for formation movement and direction changes |
| 331 | + (swap! game-state |
| 332 | + (fn [state] |
| 333 | + (cond |
| 334 | + (and (>= new-x 30) (= direction 1)) |
| 335 | + (-> state |
| 336 | + (assoc :formation-direction -1) |
| 337 | + (assoc :formation-offset {:x 30 :y y})) |
| 338 | + (and (<= new-x -30) (= direction -1)) |
| 339 | + (-> state |
| 340 | + (assoc :formation-direction 1) |
| 341 | + (assoc :formation-offset {:x -30 :y y})) |
| 342 | + :else |
| 343 | + (assoc state :formation-offset {:x new-x :y y}))))) |
337 | 344 | ;; Update enemies |
338 | 345 | (swap! game-state update :enemies |
339 | 346 | (fn [enemies] |
|
380 | 387 | enemy enemies] |
381 | 388 | (when (and (collides? :a bullet :b enemy) |
382 | 389 | (not= (:state enemy) :destroyed)) |
383 | | - ;; Remove bullet |
384 | | - (swap! game-state update :bullets |
385 | | - #(vec (remove (fn [b] (= b bullet)) %))) |
386 | | - ;; Damage enemy |
387 | | - (let [new-hits (dec (:hits enemy))] |
388 | | - (if (<= new-hits 0) |
389 | | - ;; Destroy enemy |
390 | | - (do |
391 | | - (play-explosion-sound) ; Play explosion sound |
392 | | - (swap! game-state update :enemies |
393 | | - #(vec (remove (fn [e] (= e enemy)) %))) |
394 | | - (swap! game-state update :score + (:points enemy)) |
395 | | - (swap! game-state update :particles |
396 | | - #(vec (concat % (create-particles :x (:x enemy) |
397 | | - :y (:y enemy) |
398 | | - :count 10 |
399 | | - :color (:color enemy)))))) |
400 | | - ;; Damage enemy |
401 | | - (swap! game-state update :enemies |
402 | | - #(mapv (fn [e] |
403 | | - (if (= e enemy) |
404 | | - (assoc e :hits new-hits) |
405 | | - e)) |
406 | | - %)))))) |
| 390 | + (let [new-hits (dec (:hits enemy)) |
| 391 | + destroyed? (<= new-hits 0)] |
| 392 | + (when destroyed? |
| 393 | + (play-explosion-sound)) ; Play explosion sound |
| 394 | + ;; Single atomic update for all collision effects |
| 395 | + (swap! game-state |
| 396 | + (fn [state] |
| 397 | + (-> state |
| 398 | + ;; Remove bullet |
| 399 | + (update :bullets #(vec (remove (fn [b] (= b bullet)) %))) |
| 400 | + ;; Handle enemy destruction or damage |
| 401 | + (update :enemies |
| 402 | + (fn [enemies] |
| 403 | + (if destroyed? |
| 404 | + ;; Destroy enemy |
| 405 | + (vec (remove (fn [e] (= e enemy)) enemies)) |
| 406 | + ;; Damage enemy |
| 407 | + (mapv (fn [e] |
| 408 | + (if (= e enemy) |
| 409 | + (assoc e :hits new-hits) |
| 410 | + e)) |
| 411 | + enemies)))) |
| 412 | + ;; Add score if destroyed |
| 413 | + (#(if destroyed? |
| 414 | + (update % :score + (:points enemy)) |
| 415 | + %)) |
| 416 | + ;; Add particles if destroyed |
| 417 | + (#(if destroyed? |
| 418 | + (update % :particles |
| 419 | + (fn [particles] |
| 420 | + (vec (concat particles |
| 421 | + (create-particles :x (:x enemy) |
| 422 | + :y (:y enemy) |
| 423 | + :count 10 |
| 424 | + :color (:color enemy)))))) |
| 425 | + %)))))))) |
407 | 426 | ;; Check enemy bullet-player collisions |
408 | 427 | (doseq [bullet enemy-bullets] |
409 | 428 | (when (collides? :a bullet :b player) |
410 | 429 | (play-hit-sound) ; Play hit sound |
411 | | - (swap! game-state update :enemy-bullets |
412 | | - #(vec (remove (fn [b] (= b bullet)) %))) |
413 | | - (swap! game-state update-in [:player :lives] dec) |
414 | | - (swap! game-state update :particles |
415 | | - #(vec (concat % (create-particles :x (:x player) |
416 | | - :y (:y player) |
417 | | - :count 15 |
418 | | - :color "#FFFF00")))) |
419 | | - (when (<= (get-in @game-state [:player :lives]) 0) |
420 | | - (swap! game-state assoc :game-status :game-over) |
421 | | - (swap! game-state update :high-score max (:score @game-state))))) |
| 430 | + ;; Single atomic update for all hit effects |
| 431 | + (let [new-lives (dec (:lives player)) |
| 432 | + game-over? (<= new-lives 0)] |
| 433 | + (swap! game-state |
| 434 | + (fn [state] |
| 435 | + (-> state |
| 436 | + ;; Remove bullet |
| 437 | + (update :enemy-bullets #(vec (remove (fn [b] (= b bullet)) %))) |
| 438 | + ;; Decrement lives |
| 439 | + (update-in [:player :lives] dec) |
| 440 | + ;; Add particles |
| 441 | + (update :particles |
| 442 | + #(vec (concat % (create-particles :x (:x player) |
| 443 | + :y (:y player) |
| 444 | + :count 15 |
| 445 | + :color "#FFFF00")))) |
| 446 | + ;; If game over, update status and high score |
| 447 | + (#(if game-over? |
| 448 | + (-> % |
| 449 | + (assoc :game-status :game-over) |
| 450 | + (update :high-score max (:score %))) |
| 451 | + %)))))))) |
422 | 452 | ;; Check for wave clear |
423 | 453 | (when (empty? enemies) |
424 | 454 | (play-wave-complete-sound) ; Play victory sound |
|
0 commit comments