;;; Lisparuga ;;; Copyright © 2020 David Thompson ;;; ;;; Lisparuga is free software: you can redistribute it and/or modify ;;; it under the terms of the GNU General Public License as published ;;; by the Free Software Foundation, either version 3 of the License, ;;; or (at your option) any later version. ;;; ;;; Lisparuga is distributed in the hope that it will be useful, but ;;; WITHOUT ANY WARRANTY; without even the implied warranty of ;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ;;; General Public License for more details. ;;; ;;; You should have received a copy of the GNU General Public License ;;; along with Lisparuga. If not, see . ;;; Commentary: ;; ;; Enemy actors. ;; ;;; Code: (define-module (lisparuga enemy) #:use-module (chickadee audio) #: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 (lisparuga actor) #:use-module (lisparuga asset) #:use-module (lisparuga bullets) #:use-module (lisparuga config) #:use-module (lisparuga node) #:use-module (lisparuga node-2d) #:use-module (oop goops) #:export ( health points parting-shots dead? fire-parting-shots-maybe make-utatsugumi make-toratsugumi make-renjyaku)) ;;; ;;; Base Enemy ;;; (define-asset explosion-sound (load-audio (scope-asset "sounds/explosion.wav"))) (define-asset hit-sound (load-audio (scope-asset "sounds/hit.wav"))) (define-class () (health #:accessor health #:init-keyword #:health) (points #:getter points #:init-keyword #:points) (parting-shots #:getter parting-shots #:init-keyword #:parting-shots) (fire-parting-shots? #:accessor fire-parting-shots? #:init-form #f)) (define-method (damage (enemy ) x) (let ((new-health (max (- (health enemy) x) 0))) (set! (health enemy) new-health) (if (zero? new-health) (audio-play (asset-ref explosion-sound) #:volume 0.5) (audio-play (asset-ref hit-sound) #:volume 0.5)))) (define-method (dead? (enemy )) (zero? (health enemy))) (define (fire-parting-shots-maybe enemy player) (when (fire-parting-shots? enemy) (let* ((n (parting-shots enemy)) (ep (position enemy)) (pp (position player)) (angle-to-player (atan (- (vec2-y pp) (vec2-y ep)) (- (vec2-x pp) (vec2-x ep))))) (let loop ((i 0)) (when (< i n) (let ((theta (+ angle-to-player (- (* (random:uniform) (/ pi 4.0)) (/ pi 8.0))))) (spawn-bullet (bullet-field enemy) small-dot (polarity enemy) (+ (vec2-x ep) (- (* (random:uniform) 16.0) 8.0)) (+ (vec2-y ep) (- (* (random:uniform) 16.0) 8.0)) (* (cos theta) 4.0) (* (sin theta) 4.0))) (loop (+ i 1))))))) (define-method (on-collision (enemy ) bullet bullet-polarity hitbox) ;; TODO: Distinguish between normal play bullets and homing shots ;; that do more damage. ;; ;; Same polarity = 1 point of damage ;; Opposite polarity = 2 points of damage (let ((same-polarity? (eq? bullet-polarity (polarity enemy)))) (damage enemy (if same-polarity? 1 2)) (when (and same-polarity? (dead? enemy)) (set! (fire-parting-shots? enemy) #t))) #t) (define-method (load-enemy-atlas file-name) (let ((texture (load-image file-name)) (enemy-tiles ;; 0: Utatsugumi - white `((0.0 0.0 24.0 24.0) ;; 1: Utatsugumi - black (24.0 0.0 24.0 24.0) ;; 2: Toratsugumi - white (0.0 24.0 24.0 24.0) ;; 3: Toratsugumi - black (24.0 24.0 24.0 24.0) ;; 4: Renjyaku - white (48.0 0.0 48.0 48.0) ;; 5: Renjyaku - black (96.0 0.0 48.0 48.0)))) (list->texture-atlas texture enemy-tiles))) (define-asset enemy-atlas (load-enemy-atlas (scope-asset "images/enemies.png"))) ;;; ;;; Utatsugumi ;;; (define-class ()) (define-method (on-boot (utatsugumi )) (attach-to utatsugumi (make #:atlas enemy-atlas #:index (if (eq? 'white (polarity utatsugumi)) 0 1) #:origin (vec2 12.0 12.0)))) (define (make-utatsugumi polarity x y) (make #:name (gensym "utatsugumi-") #:health 1 #:points 20 #:parting-shots 5 #:polarity polarity #:hitboxes (list (make-hitbox 'utatsugumi (make-rect -10.0 -10.0 20.0 20.0))) #:position (vec2 x y))) ;;; ;;; Toratsugumi ;;; (define-class ()) (define-method (on-boot (toratsugumi )) (attach-to toratsugumi (make #:atlas enemy-atlas #:index (if (eq? 'white (polarity toratsugumi)) 2 3) #:origin (vec2 12.0 12.0)))) (define (make-toratsugumi polarity x y) (make #:name (gensym "toratsugumi-") #:health 1 #:points 20 #:parting-shots 5 #:polarity polarity #:hitboxes (list (make-hitbox 'toratsugumi (make-rect -5.5 -5.5 11.0 11.0))) #:position (vec2 x y))) ;;; ;;; Renjyaku ;;; (define-class ()) (define-method (on-boot (renjyaku )) (attach-to renjyaku (make #:atlas enemy-atlas #:index (if (eq? 'white (polarity renjyaku)) 4 5) #:origin (vec2 24.0 24.0)))) (define (make-renjyaku polarity x y) (make #:name (gensym "renjyaku-") #:health 20 #:points 100 #:parting-shots 7 #:polarity polarity #:hitboxes (list (make-hitbox 'renjyaku (make-rect -16.0 -12.0 32.0 25.0))) #:position (vec2 x y)))