#lang s-exp "../../moby-lang.ss"
(require 2htdp/universe
lang/prim
lang/posn
(except-in htdp/testing test)
(for-syntax scheme/base))
(provide START
EXAMPLE
test-frame score sq sine cosine tangent
(except-out (all-from-out 2htdp/universe) on-key on-mouse))
(define-higher-order-primitive START animate/proc (title title-color
background objectImgs targetImgs playerImg projectileImg
direction
update-player update-target update-object update-projectile
collide? in-domain?))
(define WIDTH 640)
(define HEIGHT 480)
(define EXPLOSION-COLOR "gray")
(define TITLE-COLOR "white")
(define PROJECTILE-IMG (star 5 20 30 "solid" "yellow"))
(define BACKGROUND (rectangle WIDTH HEIGHT "solid" "black"))
(define DIRECTION "left")
(define score 0)
(define (spacing) (random 500))
(define-struct being [posn costume])
(define-struct world [objects targets player projectiles bg score title timer])
(define being-x (compose posn-x being-posn))
(define being-y (compose posn-y being-posn))
(define (posn->point posn) (make-posn (posn-x posn) (+ HEIGHT (- (posn-y posn)))))
(define (draw-being being background)
(let ((screen-posn (posn->point (being-posn being))))
(place-image (being-costume being)
(posn-x screen-posn) (posn-y screen-posn)
background)))
(define (draw-world w)
(let* ((score-string (string-append (world-title w) " score:" (number->string (world-score w))))
(player (if (> (world-timer w) 0)
(make-being (being-posn (world-player w))
(star 7 (* 1.5 (world-timer w)) (* .25 (world-timer w)) "solid" EXPLOSION-COLOR))
(world-player w)))
(all-beings (append (world-targets w) (world-objects w) (world-projectiles w) (list player))))
(place-image (text score-string 20 TITLE-COLOR) 10 0
(foldl draw-being (put-pinhole BACKGROUND 0 0) all-beings))))
(define (wrap-update f)
(cond
[(and (= (procedure-arity f) 1) (member DIRECTION (list "top" "bottom")))
(lambda (b) (make-being (make-posn (being-x b) (f (being-y b))) (being-costume b)))]
[(and (= (procedure-arity f) 1) (member DIRECTION (list "left" "right")))
(lambda (b) (make-being (make-posn (f (being-x b)) (being-y b)) (being-costume b)))]
[else (lambda (b) (make-being (f (being-x b) (being-y b)) (being-costume b)))]))
(define (reset being)
(make-being
(cond
[(string=? DIRECTION "left") (make-posn (* (spacing) -1) (random HEIGHT))]
[(string=? DIRECTION "right") (make-posn (+ (spacing) WIDTH) (random HEIGHT))]
[(string=? DIRECTION "top") (make-posn (random WIDTH) (+ (spacing) HEIGHT))]
[(string=? DIRECTION "bottom") (make-posn (random WIDTH) (* (spacing) -1))])
(being-costume being)))
(define (move-all beings move in-domain?)
(map (lambda (b) (if (in-domain? (being-x b) (being-y b)) (move b) (reset b))) beings))
(define (keypress w key update-player)
(cond
[(string=? key " ")
(make-world (world-objects w)
(world-targets w)
(world-player w)
(cons (make-being (being-posn (world-player w)) PROJECTILE-IMG)
(if (= 20 (length (world-projectiles w)))
(rest (world-projectiles w))
(world-projectiles w)))
(world-bg w)
(world-score w)
(world-title w)
(world-timer w))]
[else
(make-world (world-objects w)
(world-targets w)
(update-player (world-player w) key)
(world-projectiles w)
(world-bg w)
(world-score w)
(world-title w)
(world-timer w))]))
(define (any-collide? collide? t beings)
(not (empty? (filter (lambda (b) (collide? b t)) beings))))
(define (check-collision beings projectiles collide?)
(map (lambda (being) (if (any-collide? collide? being projectiles)
(reset being)
being))
beings))
(define (START title title-color
background objectImgs targetImgs playerImg projectileImg
direction
update-player* update-target* update-object* update-projectile*
collide*? in-domain*?)
(begin
(set! PROJECTILE-IMG projectileImg)
(set! TITLE-COLOR title-color)
(set! BACKGROUND background)
(set! DIRECTION direction)
(let* ((player (make-being (make-posn (/ WIDTH 2) (/ HEIGHT 2)) playerImg))
(targetImgs (if (list? targetImgs) targetImgs (list targetImgs)))
(objectImgs (if (list? objectImgs) objectImgs (list objectImgs)))
(targets (map (lambda (t) (make-being (make-posn (- 0 (spacing)) (random HEIGHT)) t)) targetImgs))
(objects (map (lambda (o) (make-being (make-posn (- 0 (spacing)) (random HEIGHT)) o)) objectImgs))
(projectiles empty)
(update-object (wrap-update update-object*))
(update-target (wrap-update update-target*))
(update-projectile (wrap-update update-projectile*))
(update-player (cond
[(and (= (procedure-arity update-player*) 2) (member DIRECTION (list "left" "right")))
(lambda (p k) (make-being (make-posn (being-x p) (update-player* (being-y p) k))
(being-costume p)))]
[(and (= (procedure-arity update-player*) 2) (member DIRECTION (list "top" "bottom")))
(lambda (p k) (make-being (make-posn (update-player* (being-x p) k) (being-y p))
(being-costume p)))]
[else (lambda (p k) (make-being (update-player* (being-x p) (being-y p) k)
(being-costume p)))]))
(in-domain? (if (= (procedure-arity in-domain*?) 1) (lambda (x y) (in-domain*? x)) in-domain*?))
(collide? (lambda (b1 b2) (collide*? (being-x b1) (being-y b1) (being-x b2) (being-y b2))))
(world (make-world objects targets player projectiles
(put-pinhole background 0 0)
0
title
0))
(keypress* (lambda (w k) (keypress w k update-player)))
(update-world (lambda (w)
(begin
(set! score (world-score w))
(let* ((objects (move-all (check-collision (world-objects w) (world-projectiles w) collide?)
update-object in-domain?))
(targets (move-all (world-targets w) update-target in-domain?))
(projectiles (move-all (world-projectiles w) update-projectile in-domain?))
(score (world-score w))
(player (world-player w))
(bg (world-bg w))
(title (world-title w))
(timer (world-timer w)))
(cond
[(> timer 0)
(make-world objects targets player projectiles bg score title (- timer 10))]
[(any-collide? collide? player objects)
(begin
(play-sound "hit.wav" true)
(make-world objects targets player projectiles bg (- score 50) title 100))]
[(any-collide? collide? player targets)
(begin
(play-sound "score.wav" true)
(make-world objects targets player projectiles bg (+ score 20) title 100))]
[else (make-world objects targets player projectiles bg score title timer)]))
))))
(js-big-bang world
(on-tick .1 update-world)
(on-redraw draw-world)
(on-key keypress*)))))
(define (test-frame title bg objectImgs targetImgs playerImg projectileImgs)
(let* ((targetImgs (if (list? targetImgs) targetImgs (list targetImgs)))
(objectImgs (if (list? objectImgs) objectImgs (list objectImgs)))
(player (make-being (make-posn 320 400) playerImg))
(targets (map (lambda (t) (make-being (make-posn (random 640) (random 480)) t)) targetImgs))
(objects (map (lambda (o) (make-being (make-posn (random 640) (random 480)) o)) objectImgs))
(projectiles (list (make-being (make-posn -200 0) PROJECTILE-IMG)))
(world (make-world objects targets player projectiles
(put-pinhole bg 0 0)
100
title
0)))
(draw-world world)))
(define (sq x) (* x x))
(define (sine x) (sin (* x (/ pi 180))))
(define (cosine x) (cos (* x (/ pi 180))))
(define (tangent x) (tan (* x (/ pi 180))))
(require (for-syntax syntax/kerncase))
(define-syntax (EXAMPLE stx)
(syntax-case stx ()
[(_ x ...)
(with-handlers ([exn? (lambda (e)
(raise (make-exn
(regexp-replace*
#rx"check-expect"
(exn-message e)
"test")
(exn-continuation-marks e))))])
(local-expand (syntax/loc stx (check-expect x ...))
(syntax-local-context)
(kernel-form-identifier-list)))]))