#lang scheme
(require 2htdp/universe
"posn.ss"
"structs.ss")
(define-struct body (posn velocity
speed-change speed-change-frames
dir-change dir-change-frames
accel accel-frames
previous-fire params action) #:mutable)
(define top-label "top")
(define height 600)
(define width 800)
(define tick-rate 4/60)
(define (on-screen? p)
(define cp (posn->cartesian-posn p))
(and (< 0 (cartesian-posn-x cp) width)
(< 0 (cartesian-posn-y cp) height)))
(define current-rank (make-parameter 1))
(define current-params (make-parameter (vector)))
(define-struct simulation (frames bullets))
(define Expression->value
(match-lambda
[(struct Expression (value-f))
(value-f (current-rank) (current-params))]))
(define (value->Expression v)
(make-Expression (lambda (rank params) v)))
(define (Expression-fun f e)
(value->Expression (f (Expression->value e))))
(define (Expression-zero? e)
(zero? (Expression->value e)))
(define term-fun
(match-lambda*
[(list f (struct term (how-long-e)))
(make-term (Expression-fun f how-long-e))]))
(define times-fun
(match-lambda*
[(list f (struct times (how-long-e)))
(make-times (Expression-fun f how-long-e))]))
(define times-zero?
(match-lambda
[(struct times (how-long-e))
(Expression-zero? how-long-e)]))
(define-struct saved-params (pvector inner))
(define (load-params params)
(apply vector
(map (match-lambda
[(struct param (expr))
(Expression->value expr)])
params)))
(define (posn-speed p)
(polar-posn-r (posn->polar-posn p)))
(define (body-speed b)
(posn-speed (body-velocity b)))
(define (body-previous-fire-speed b)
(posn-speed (body-previous-fire b)))
(define (posn-direction p)
(polar-posn-theta (posn->polar-posn p)))
(define (body-direction b)
(posn-direction (body-velocity b)))
(define (body-previous-fire-direction b)
(posn-direction (body-previous-fire b)))
(define (speed->value rel seq s)
(match s
[(struct speed (type expr))
(define v (Expression->value expr))
(case type
[(absolute)
v]
[(relative)
(+ v rel)]
[(sequence)
(+ v seq)])]))
(define (direction->value aim rel seq d)
(match d
[(struct direction (type expr))
(define v (degrees->radians (Expression->value expr)))
(case type
[(aim)
(+ v aim)]
[(absolute)
v]
[(relative)
(+ v rel)]
[(sequence)
(+ v seq)])]))
(define (aim-at p1 p2)
(define cp1 (posn->cartesian-posn p1))
(define cp2 (posn->cartesian-posn p2))
(define dx
(- (cartesian-posn-x cp1)
(cartesian-posn-x cp2)))
(define dy
(- (cartesian-posn-y cp1)
(cartesian-posn-y cp2)))
(atan dy dx))
(define (place-at-posn i p s)
(define cp (posn->cartesian-posn p))
(place-image i
(cartesian-posn-x cp)
(cartesian-posn-y cp)
s))
(define (simulate bml)
(define bullets (make-hash))
(define actions (make-hash))
(define fires (make-hash))
(define bullet-or-ref->bullet
(match-lambda
[(? bullet? b)
(values b (current-params))]
[(struct bulletRef (label params))
(values (hash-ref bullets label)
(load-params params))]))
(define target-posn
(make-cartesian-posn
(/ width 2)
(/ height 2)))
(define bs empty)
(define (act! a b)
(match a
[(struct repeat (times inner))
(define new-times (times-fun (compose round sub1) times))
(if (times-zero? new-times)
inner
(make-action (new-label 'repeat)
(list inner
(make-repeat new-times inner))))]
[(struct fire (label direction speed bullet-or-ref))
(define-values (bullet params) (bullet-or-ref->bullet bullet-or-ref))
(define prv-speed (body-previous-fire-speed b))
(define aim-rad (aim-at target-posn (body-posn b)))
(define the-speed
(if speed
(speed->value prv-speed prv-speed speed)
(if (bullet-speed bullet)
(speed->value prv-speed prv-speed (bullet-speed bullet))
1)))
(define the-dir
(if direction
(direction->value aim-rad (body-direction b) (body-previous-fire-direction b) direction)
(if (bullet-direction bullet)
(direction->value aim-rad (body-direction b) (body-previous-fire-direction b) (bullet-direction bullet))
aim-rad)))
(define new-velocity
(make-polar-posn the-speed the-dir))
(set-body-previous-fire! b new-velocity)
(set! bs
(list*
(make-body (body-posn b) new-velocity
0 0
0 0
(make-cartesian-posn 0 0) 0
(make-polar-posn 0 0)
params
(make-action (new-label 'bullet) (bullet-actions bullet)))
bs))
#f]
[(struct fireRef (label params))
(act! (make-saved-params (load-params params)
(hash-ref fires label)) b)]
[(struct changeSpeed (speed term))
(define term-v (round (Expression->value (term-how-many term))))
(define speed-v (speed->value (body-speed b) 0 speed))
(define change-in-speed
(/ (- speed-v (body-speed b))
term-v))
(set-body-speed-change! b change-in-speed)
(set-body-speed-change-frames! b term-v)
#f]
[(struct changeDirection (direction term))
(define term-v (round (Expression->value (term-how-many term))))
(define aim-rad (aim-at target-posn (body-posn b)))
(define direction-v (direction->value aim-rad (body-direction b) 0 direction))
(define change-in-direction
(if (eq? 'sequence (direction-type direction))
direction-v
(/ (- direction-v (body-direction b))
term-v)))
(set-body-dir-change! b change-in-direction)
(set-body-dir-change-frames! b term-v)
#f]
[(struct accel (hor vert term))
(define term-v (round (Expression->value (term-how-many term))))
(define dx
(if hor
(local [(define dx-v (Expression->value (horizontal-units hor)))]
(case (horizontal-type hor)
[(absolute)
(/ (- dx-v (cartesian-posn-x (body-accel b)))
term-v)]
[(relative)
(/ dx-v term-v)]
[(sequence)
dx-v]))
0))
(define dy
(if vert
(local [(define dy-v (Expression->value (vertical-units vert)))]
(case (vertical-type vert)
[(absolute)
(/ (- dy-v (cartesian-posn-y (body-accel b)))
term-v)]
[(relative)
(/ dy-v term-v)]
[(sequence)
dy-v]))
0))
(set-body-accel! b (make-cartesian-posn dx dy))
(set-body-accel-frames! b term-v)
#f]
[(struct wait (frames))
(define new-frames (Expression-fun (compose round sub1) frames))
(if (Expression-zero? new-frames)
#f
(make-wait new-frames))]
[(struct vanish ())
(set! bs (remq b bs))
#f]
[(struct action (label steps))
(if (empty? steps)
#f
(local [(define new-step (act! (first steps) b))]
(if new-step
(make-action label (list* new-step (rest steps)))
(if (empty? (rest steps))
#f
(make-action label (rest steps))))))]
[(struct actionRef (label params))
(act! (make-saved-params (load-params params)
(hash-ref actions label)) b)]
[(struct saved-params (params inner))
(define new-inner
(parameterize ([current-params params])
(act! inner b)))
(if new-inner
(make-saved-params params new-inner)
#f)]
[#f
#f]))
(define (body-move! b)
(define new-action
(parameterize ([current-params (body-params b)])
(act! (body-action b) b)))
(unless (zero? (body-speed-change-frames b))
(set-body-speed-change-frames! b (sub1 (body-speed-change-frames b)))
(set-body-velocity! b (posn+ (body-velocity b) (make-polar-posn (body-speed-change b) 0))))
(unless (zero? (body-dir-change-frames b))
(set-body-dir-change-frames! b (sub1 (body-dir-change-frames b)))
(set-body-velocity! b (posn+ (body-velocity b) (make-polar-posn 0 (body-dir-change b)))))
(unless (zero? (body-accel-frames b))
(set-body-accel-frames! b (sub1 (body-accel-frames b)))
(set-body-velocity! b (posn+ (body-velocity b) (body-accel b))))
(set-body-posn! b (posn+ (body-posn b) (body-velocity b)))
(set-body-action! b new-action))
(define (stop-bullets? frame)
(and (> frame 10) (= 1 (length bs)))
(empty? bs))
(define (draw-bullets frame)
(printf "~n")
(for/fold ([s (place-at-posn (circle 3 'outline "red")
target-posn
(place-image (text (format "Bullets: ~a" (length bs))
12 "black")
0 0
(empty-scene width height)))])
([b (in-list bs)])
(place-at-posn (circle 2 'solid "black")
(body-posn b)
s)))
(define (tick-bullets frame)
(define next-frame (add1 frame))
(for ([b (in-list bs)])
(body-move! b))
(set! bs
(filter (lambda (b)
(on-screen? (body-posn b)))
bs))
next-frame)
(define (move-target frame x y me)
(set! target-posn (make-cartesian-posn x y))
frame)
(define top-posn
(case (bulletml-type bml)
[(vertical)
(make-cartesian-posn (/ width 2) 10)]
[(horizontal)
(make-cartesian-posn (- width 10) (/ height 2))]
[else
(make-cartesian-posn (/ width 2) 10)]))
(for ([e (in-list (bulletml-contents bml))])
(match e
[(? bullet?)
(hash-set! bullets (bullet-label e) e)]
[(? action?)
(hash-set! actions (action-label e) e)]
[(? fire?)
(hash-set! fires (fire-label e) e)]))
(set! bs (list* (make-body top-posn (make-polar-posn 0 0)
0 0
0 0
(make-cartesian-posn 0 0) 0
(make-polar-posn 0 0) (vector)
(hash-ref actions top-label))
bs))
(big-bang
0
(on-tick tick-bullets
tick-rate)
(on-draw draw-bullets)
(on-mouse move-target)
(stop-when stop-bullets?)))
(provide/contract
[current-rank (parameter/c (real-in 0 1))]
[simulate (bulletml? . -> . void)])