dynablaster.rkt
#lang racket

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

(provide  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 (sprite-random (world-decor w) 0 0 IMAGE-EMPTY 'player ENERGY-PLAYER) (rest d)))]
                    [(and (robot? (first d)) 
                          (energy0? (first d))) (begin ; the robot dies
                                                  (set! score-player (+ score-player 1)) 
                                                  (cons (sprite-random (world-decor w) 0 0 IMAGE-EMPTY 'robot ENERGY-ROBOT) (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))])    
    (make-world (move-player (player d)
                             (move-robot (robot d) d)) 
                (world-move-robot w) (world-move-player w)
                score-player score-robot (world-with-sound w))))

; 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)]   
         [mr (world-move-robot w)]
         [mp (world-move-player w)]
         [sp (world-score-player w)]
         [sr (world-score-robot w)]
         [sd (world-with-sound w)])    
    (if (empty? j)
        w
        (cond
          [(string=? s "up") (make-world (go-up j d) mr mp sp sr sd)]
          [(string=? s "down") (make-world (go-down j d) mr mp sp sr sd)]
          [(string=? s "left") (make-world (go-left j d) mr mp sp sr sd)]
          [(string=? s "right") (make-world (go-right j d) mr mp sp sr sd)]
          [(string=? s " ") (make-world (drop-bomb j d) mr mp sp sr sd)]
          [else w]))))

(define (initial-world move-robot move-player sound) 
  (make-world (make-decor (make-decor (new-robot (new-player BORDER))
                                      50 
                                      IMAGE-ROCK #f 'rock 0)
                          100
                          IMAGE-BRICK #f 'brick 0)
              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)