summaryrefslogtreecommitdiff
path: root/lisparuga/player.scm
diff options
context:
space:
mode:
Diffstat (limited to 'lisparuga/player.scm')
-rw-r--r--lisparuga/player.scm152
1 files changed, 133 insertions, 19 deletions
diff --git a/lisparuga/player.scm b/lisparuga/player.scm
index a810e48..ca57891 100644
--- a/lisparuga/player.scm
+++ b/lisparuga/player.scm
@@ -21,14 +21,17 @@
;;; Code:
(define-module (lisparuga player)
+ #:use-module (chickadee math)
#:use-module (chickadee math rect)
#:use-module (chickadee math vector)
#:use-module (chickadee scripting)
#:use-module (chickadee render texture)
+ #:use-module (ice-9 match)
#:use-module (lisparuga actor)
#:use-module (lisparuga asset)
#:use-module (lisparuga bullets)
#:use-module (lisparuga config)
+ #:use-module (lisparuga enemy)
#:use-module (lisparuga node)
#:use-module (lisparuga node-2d)
#:use-module (oop goops)
@@ -44,21 +47,23 @@
start-shooting
stop-shooting
toggle-polarity
- fire-homing-missiles))
+ fire-homing-missiles
+ kill-maybe
+ on-kill))
(define-asset ship (load-image (scope-asset "images/player.png")))
(define-asset ship-atlas (load-tile-atlas (scope-asset "images/player.png") 24 24))
-(define kill-hitbox (make-hitbox 'kill (make-rect 0.0 0.0 0.0 0.0)))
-(define graze-hitbox (make-hitbox 'graze (make-rect 0.0 0.0 0.0 0.0)))
+(define kill-hitbox (make-hitbox 'kill (make-rect -2.0 -2.0 4.0 4.0)))
+(define graze-hitbox (make-hitbox 'graze (make-rect -12.0 -12.0 24.0 24.0)))
(define-class <player> (<actor>)
(score #:accessor score #:init-value 0)
- (lives #:accessor lives #:init-value 2)
+ (lives #:accessor lives #:init-value 3)
(energy #:accessor energy #:init-value 0)
(chain #:accessor chain #:init-value 0)
(chain-progress #:accessor chain-progress #:init-form '())
- (speed #:accessor speed #:init-value 1.75)
+ (speed #:accessor speed #:init-value 2.5)
(invincible? #:accessor invincible? #:init-value #f)
(shooting? #:accessor shooting? #:init-value #f)
(shoot-time #:accessor shoot-time #:init-value 0))
@@ -153,11 +158,75 @@
;; Change sprite
(set! (index (& player ship)) (if (eq? old 'white) 4 0))))))
-(define-method (fire-homing-missiles (player <player>))
+(define-method (fire-homing-missiles (player <player>) enemies)
(let* ((e (energy player))
- (n (quotient e 10)))
- (set! (energy player) (- e (* n 10)))
- ;; TODO: search for nearest enemy and fire missiles
+ (n (quotient e 10))
+ (p (position player))
+ (bullets (bullet-field player)))
+ (define (distance-to-player enemy)
+ ;; We don't need the true distance here so no need to use an
+ ;; expensive sqrt call.
+ (let ((ep (position enemy)))
+ (+ (expt (- (vec2-x ep) (vec2-x p)) 2)
+ (expt (- (vec2-y ep) (vec2-y p)) 2))))
+ (define (find-closest-enemy enemies)
+ (let loop ((enemies enemies)
+ (closest-enemy #f)
+ (distance 999999999.0))
+ (match enemies
+ ((enemy . rest)
+ (if (dead? enemy)
+ (loop rest closest-enemy distance)
+ (let ((d (distance-to-player enemy)))
+ (if (< d distance)
+ (loop rest enemy d)
+ (loop rest closest-enemy distance)))))
+ (()
+ closest-enemy))))
+ (define (fire-missiles n enemy)
+ (let ((speed 10.0))
+ (if enemy
+ (let* ((ep (position enemy)))
+ (define (aim-at-enemy bp bv)
+ (let ((dir (atan (- (vec2-y ep) (vec2-y bp))
+ (- (vec2-x ep) (vec2-x bp)))))
+ (set-vec2! bv (* (cos dir) speed) (* (sin dir) speed))))
+ (run-script player
+ (let loop ((i 0))
+ (when (< i n)
+ (spawn-bullet bullets ikaruga-missile (polarity player)
+ (vec2-x p) (vec2-y p) 0.0 0.0 aim-at-enemy)
+ (sleep 3)
+ (loop (+ i 1))))))
+ (let loop ((i 0))
+ (when (< i n)
+ (let ((theta (+ (* (random:uniform) .5 pi)
+ (* .25 pi))))
+ (spawn-bullet bullets ikaruga-missile (polarity player)
+ (vec2-x p) (vec2-y p)
+ (* (cos theta) speed) (* (sin theta) speed))
+ (loop (+ i 1))))))))
+ ;; Distribute missiles amongst closest enemies
+ (let loop ((enemies enemies)
+ (missiles-remaining (quotient e 10))
+ (missiles-used 0))
+ (if (zero? missiles-remaining)
+ (set! (energy player) (- e (* missiles-used 10)))
+ (let ((closest-enemy (find-closest-enemy enemies)))
+ (if closest-enemy
+ ;; Either kill the enemy or use all missiles.
+ (let ((missiles-to-fire (min (inexact->exact
+ (ceiling
+ (/ (health closest-enemy) 10.0)))
+ missiles-remaining)))
+ (fire-missiles missiles-to-fire closest-enemy)
+ (loop (delq closest-enemy enemies)
+ (- missiles-remaining missiles-to-fire)
+ (+ missiles-used missiles-to-fire)))
+ ;; No enemy available, fire missiles into the void
+ (begin
+ (fire-missiles missiles-remaining #f)
+ (loop enemies 0 (+ missiles-used missiles-remaining)))))))
#t))
(define-method (increment-energy (player <player>))
@@ -165,18 +234,24 @@
(define-method (kill-maybe (player <player>))
(unless (invincible? player)
- (set! (lives player) (- (lives player) 1))
- ;; Give player invincibility for a bit while they recover.
- (run-script player
- (set! (invincible? player) #t)
- ;; 3 seconds of blinking
- (blink 18 5)
- (set! (invincible? player) #f))))
+ (let ((new-lives (- (lives player) 1)))
+ (set! (lives player) new-lives)
+ (if (zero? new-lives)
+ (hide player)
+ ;; Give player invincibility for a bit while they recover.
+ (run-script player
+ (set! (invincible? player) #t)
+ ;; 3 seconds of blinking
+ (blink player 18 5)
+ (set! (invincible? player) #f))))))
(define-method (on-collision (player <player>) (other <actor>)
hitbox other-hitbox)
- (when (eq? hitbox kill-hitbox)
- (kill-maybe player)))
+ (if (eq? hitbox kill-hitbox)
+ (begin
+ (kill-maybe player)
+ #t)
+ #f))
(define-method (on-collision (player <player>) bullet bullet-polarity hitbox)
(cond
@@ -184,9 +259,48 @@
((and (eq? hitbox graze-hitbox)
(eq? bullet-polarity (polarity player)))
(increment-energy player)
+ ;; From what I can tell by watching youtube replays at .25 speed,
+ ;; each bullet absorbed is worth 100 points.
+ (set! (score player) (+ (score player) 100))
#t)
;; If a bullet makes it to the kill hitbox, lose a life.
- ((eq? hitbox kill-hitbox)
+ ((and (eq? hitbox kill-hitbox)
+ (not (invincible? player)))
(kill-maybe player)
#t)
(else #f)))
+
+(define (add-to-chain player polarity)
+ (let ((current-chain (cons polarity (chain-progress player))))
+ (match current-chain
+ ;; complete chain.
+ ((or ('white 'white 'white)
+ ('black 'black 'black))
+ (let ((new-chain (+ (chain player) 1)))
+ (set! (chain player) new-chain)
+ (set! (chain-progress player) '())
+ (set! (score player)
+ (+ (score player)
+ ;; Chain formula yields these results:
+ ;;
+ ;; - 1 Chain --- 100 points
+ ;; - 2 Chain --- 200 points
+ ;; - 3 Chain --- 400 points
+ ;; - 4 Chain --- 800 points
+ ;; - 5 Chain --- 1,600 points
+ ;; - 6 Chain --- 3,200 points
+ ;; - 7 Chain --- 6,400 points
+ ;; - 8 Chain --- 12,800 points
+ ;; - 9+ Chain -- 25,600 points
+ (* (expt 2 (- (min new-chain 9) 1)) 100)))))
+ ;; 1st or 2nd kill of the chain.
+ ((or ('white) ('black) ('white 'white) ('black 'black))
+ (set! (chain-progress player) current-chain))
+ ;; failed chain, start over.
+ (_
+ (set! (chain-progress player) '())
+ (set! (chain player) 0)))))
+
+(define-method (on-kill (player <player>) (enemy <enemy>))
+ (set! (score player) (+ (score player) (points enemy)))
+ (add-to-chain player (polarity enemy)))