dynablaster.rkt
#lang racket

(require 2htdp/universe 2htdp/image "engine.rkt" "robot.rkt" "config.rkt")

(provide world sprite
         sprite-left sprite-right sprite-up sprite-down search-sprite
         sprite-x sprite-y sprite-dx sprite-dy sprite-energy distance
         go-left go-right go-up go-down drop-bomb
         bomb? fire? player? robot? blocked? brick? rock?
         player robot
         run move-robot-default)

; at each clock tick
(define (tick w)
  (letrec ([score-player (world-score-player w)]
           [score-robot (world-score-robot w)]
           [destruction 
            (lambda (d)
              (if (empty? (search-sprite (sprite-x (first d)) 
                                         (sprite-y (first d)) 
                                         fire? 
                                         (world-decor w)))
                  (cons (first d) (tic-tac (rest d))) ; sprite not hit
                  (cond ; sprite hit
                    [(and (player? (first d)) 
                          (energy0? (first d))) (begin ; the player dies
                                                  (set! score-robot (+ score-robot 1))
                                                  (cons (new-player (rest d)) (rest d)))]
                    [(and (robot? (first d)) 
                          (energy0? (first d))) (begin ; the robot dies
                                                  (set! score-player (+ score-player 1)) 
                                                  (cons (new-robot (rest d)) (rest d)))]
                    [else (tic-tac (rest d))])))]
           [tic-tac 
            (lambda (d)
              (if (empty? (rest d))
                  d
                  (cond [(bomb? (first d)) (cons (tic-tac-bomb (first d) d w) (tic-tac (rest d)))]
                        [(fire? (first d)) (if (energy0? (first d))
                                               (tic-tac (rest d))
                                               (if (or (> (abs (sprite-dx (first d))) 0) 
                                                       (> (abs (sprite-dy (first d))) 0))
                                                   (append (spread-fire (consume (first d)) (world-decor w)) 
                                                           (tic-tac (rest d)))
                                                   (cons (consume (first d)) (tic-tac (rest d)))))]
                        [(brick? (first d)) (destruction d)]
                        [(player? (first d)) (destruction (cons (tic-tac-player-or-robot (first d) d IMAGE-PLAYER ENERGY-PLAYER) (rest d)))]
                        [(robot? (first d)) (destruction (cons (tic-tac-player-or-robot (first d) d IMAGE-ROBOT ENERGY-ROBOT) (rest d)))]
                        [else (cons (first d) (tic-tac (rest d)))])))]
           [move-robot (world-move-robot w)]
           [move-player (world-move-player w)]
           [d (tic-tac (world-decor w))])    
    (struct-copy world w 
                 [decor (move-player player (move-robot robot d))]
                 [score-player score-player] [score-robot score-robot])))

; render
(define (render w)
  (letrec ([place-sprites (lambda (sprites image)
                            (if (empty? sprites)
                                image
                                (place-image (if (fire? (first sprites))
                                                 (image-fire (first sprites)                                     
                                                             (world-decor w))
                                                 (sprite-image (first sprites)))
                                             (sprite-x (first sprites))
                                             (sprite-y (first sprites))
                                             (place-sprites (rest sprites) image))))]
           [fond-score (overlay (rectangle (* DELTA 3) (- DELTA 8) 'outline 'black)   
                                (rectangle (* DELTA 3) (- DELTA 8) 'solid 'silver))]
           [image-score (place-image (text (number->string (world-score-robot w)) 15 'red) 
                                     (- WIDTH (* DELTA 2)) (/ DELTA 2)
                                     (place-image (text (number->string (world-score-player w)) 15 'blue) 
                                                  (* DELTA 2) (/ DELTA 2)
                                                  (place-image fond-score (* DELTA 2) (/ DELTA 2)
                                                               (place-image fond-score  
                                                                            (- WIDTH (* DELTA 2)) (/ DELTA 2)
                                                                            (place-image (text "dynablaster" 15 'silver) (* DELTA 10) (/ DELTA 2)
                                                                                         (rectangle WIDTH DELTA 'solid 'dimgray))))))])           
    (above image-score
           (place-sprites (world-decor w) IMAGE-BACKGROUND))))

; keyboard handling
(define (keypress w s)
  (let* ([d (world-decor w)]
         [j (player d)])   
    (if (empty? j)
        w
        (cond
          [(string=? s "up") (struct-copy world w [decor (go-up j d)])]
          [(string=? s "down") (struct-copy world w [decor (go-down j d)])]
          [(string=? s "left") (struct-copy world w [decor (go-left j d)])]
          [(string=? s "right") (struct-copy world w [decor (go-right j d)])]
          [(string=? s " ") (struct-copy world w [decor (drop-bomb j d)])]
          [else w]))))

(define (initial-world move-robot move-player sound)
  (let ([d (make-decor (make-decor BORDER
                                   50 
                                   IMAGE-ROCK #f 'rock 0)
                       100
                       IMAGE-BRICK #f 'brick 0)])
    (world (cons (new-robot d)
                 (cons (new-player d) d))
           move-robot move-player
           0 0 sound)))

; let's go
(define (run [move-robot move-robot-default] [move-player (lambda (r d) d)] 
             #:sound [with-sound #f])
  (big-bang (initial-world move-robot move-player with-sound)
            (on-tick tick 0.1)   
            (to-draw render)
            (on-key keypress)))

; To go, type
; (run)
; or
; (run #:sound #t)
; or
; (run move-robot-default move-robot-default)