2024-04-26 18:30:59 +00:00
|
|
|
;;; Copyright (C) 2024 David Thompson <dave@spritely.institute>
|
|
|
|
;;;
|
|
|
|
;;; Licensed under the Apache License, Version 2.0 (the "License");
|
|
|
|
;;; you may not use this file except in compliance with the License.
|
|
|
|
;;; You may obtain a copy of the License at
|
|
|
|
;;;
|
|
|
|
;;; http://www.apache.org/licenses/LICENSE-2.0
|
|
|
|
;;;
|
|
|
|
;;; Unless required by applicable law or agreed to in writing, software
|
|
|
|
;;; distributed under the License is distributed on an "AS IS" BASIS,
|
|
|
|
;;; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
|
|
;;; See the License for the specific language governing permissions and
|
|
|
|
;;; limitations under the License.
|
|
|
|
|
|
|
|
;;; Commentary:
|
|
|
|
;;;
|
|
|
|
;;; Example game showing off several common game programming things.
|
|
|
|
;;;
|
|
|
|
;;; Code:
|
|
|
|
|
|
|
|
(import (scheme base)
|
|
|
|
(scheme inexact)
|
|
|
|
(hoot debug)
|
|
|
|
(hoot ffi)
|
|
|
|
(hoot hashtables)
|
|
|
|
(hoot match)
|
|
|
|
(dom canvas)
|
|
|
|
(dom document)
|
|
|
|
(dom element)
|
|
|
|
(dom event)
|
|
|
|
(dom image)
|
|
|
|
(dom media)
|
|
|
|
(dom window)
|
|
|
|
(math)
|
|
|
|
(math rect)
|
|
|
|
(math vector))
|
|
|
|
|
|
|
|
;; Data types
|
|
|
|
|
|
|
|
(define-record-type <level>
|
2024-05-18 06:07:16 +00:00
|
|
|
(make-level state player move-left? move-right? move-up? move-down?)
|
2024-04-26 18:30:59 +00:00
|
|
|
level?
|
|
|
|
(state level-state set-level-state!) ; play, win, lose
|
2024-05-18 06:07:16 +00:00
|
|
|
(player level-player)
|
2024-04-26 18:30:59 +00:00
|
|
|
(move-left? level-move-left? set-level-move-left!)
|
2024-05-18 04:31:31 +00:00
|
|
|
(move-right? level-move-right? set-level-move-right!)
|
|
|
|
(move-up? level-move-up? set-level-move-up!)
|
2024-05-18 06:07:16 +00:00
|
|
|
(move-down? level-move-down? set-level-move-down!))
|
|
|
|
|
|
|
|
(define-record-type <player>
|
|
|
|
(make-player velocity position hitbox weapon-primary)
|
|
|
|
entity?
|
|
|
|
(velocity player-velocity player-velocity-set!)
|
|
|
|
(position player-position player-position-set!)
|
|
|
|
(hitbox player-hitbox player-hitbox-set!)
|
|
|
|
(weapon-primary player-weapon-primary player-weapon-primary-set!))
|
2024-04-26 18:30:59 +00:00
|
|
|
|
|
|
|
;; Assets
|
2024-05-18 06:07:16 +00:00
|
|
|
(define image:player (make-image "assets/images/ball.png"))
|
2024-04-26 18:30:59 +00:00
|
|
|
|
|
|
|
;; Game data
|
|
|
|
(define game-width 640.0)
|
|
|
|
(define game-height 480.0)
|
2024-05-18 06:07:16 +00:00
|
|
|
(define player-width 22.0)
|
|
|
|
(define player-height 22.0)
|
|
|
|
(define player-speed 6.0)
|
2024-04-26 18:30:59 +00:00
|
|
|
|
2024-05-18 06:07:16 +00:00
|
|
|
(define (make-player* x y)
|
|
|
|
(make-player (vec2 0.0 0.0)
|
|
|
|
(vec2 x y)
|
|
|
|
(vec2 player-width player-height)
|
|
|
|
'test-shot))
|
2024-04-26 18:30:59 +00:00
|
|
|
|
|
|
|
(define (make-level-1)
|
|
|
|
(make-level 'play
|
2024-05-18 06:07:16 +00:00
|
|
|
(make-player* 0 0)
|
|
|
|
#f #f #f #f))
|
2024-04-26 18:30:59 +00:00
|
|
|
|
|
|
|
;; Game state
|
|
|
|
(define *level* (make-level-1))
|
|
|
|
|
|
|
|
(define (level-clear? level)
|
2024-05-18 06:07:16 +00:00
|
|
|
(#f))
|
2024-04-26 18:30:59 +00:00
|
|
|
|
|
|
|
(define (win! level)
|
|
|
|
(set-level-state! level 'win))
|
|
|
|
|
|
|
|
(define (lose! level)
|
|
|
|
(set-level-state! level 'lose))
|
|
|
|
|
2024-05-18 06:07:16 +00:00
|
|
|
(define (update-play-velocity! level)
|
|
|
|
(let ((speed (* player-speed
|
2024-05-18 04:31:31 +00:00
|
|
|
(+ (if (level-move-left? level) -1.0 0.0)
|
|
|
|
(if (level-move-right? level) 1.0 0.0)
|
|
|
|
(if (level-move-up? level) 0.0 1.0)
|
|
|
|
(if (level-move-down? level) 0.0 -1.0)))))
|
2024-05-18 06:07:16 +00:00
|
|
|
(set-vec2-x! (player-velocity (level-player level)) speed)
|
|
|
|
(set-vec2-y! (player-velocity (level-player level)) speed)))
|
2024-04-26 18:30:59 +00:00
|
|
|
|
|
|
|
(define dt (/ 1000.0 60.0)) ; aim for updating at 60Hz
|
|
|
|
(define (update)
|
|
|
|
(match (level-state *level*)
|
|
|
|
('play
|
2024-05-18 06:07:16 +00:00
|
|
|
(let* ((player (level-player *level*))
|
|
|
|
(p-velocity (player-velocity player))
|
|
|
|
(p-position (player-position player))
|
|
|
|
(p-hitbox (player-hitbox player)))
|
|
|
|
;; Move player
|
|
|
|
(set-vec2-x! p-position (+ (vec2-x p-position) (vec2-x p-velocity)))
|
|
|
|
(set-vec2-y! p-position (+ (vec2-y p-position) (vec2-y p-velocity)))
|
|
|
|
|
|
|
|
;; Keep player within the bounds of the game
|
2024-04-26 18:30:59 +00:00
|
|
|
(cond
|
2024-05-18 06:07:16 +00:00
|
|
|
((< (vec2-x p-position) 0.0) ; left wall
|
|
|
|
(set-vec2-x! p-position 0.0))
|
|
|
|
((> (vec2-x p-position) game-width) ; right wall
|
|
|
|
(set-vec2-x! p-position game-width)))
|
|
|
|
((< (vec2-y p-position) 0.0) ; top wall
|
|
|
|
(set-vec2-y! p-position 0.0))
|
|
|
|
((> (vec2-y p-position) game-height) ; bottom wall
|
|
|
|
(set-vec2-y! p-position game-height))))
|
2024-04-26 18:30:59 +00:00
|
|
|
(_ #t))
|
|
|
|
(timeout update-callback dt))
|
|
|
|
(define update-callback (procedure->external update))
|
|
|
|
|
|
|
|
;; Rendering
|
|
|
|
(define number->string*
|
|
|
|
(let ((cache (make-eq-hashtable))) ; assuming fixnums only
|
|
|
|
(lambda (x)
|
|
|
|
(or (hashtable-ref cache x)
|
|
|
|
(let ((str (number->string x)))
|
|
|
|
(hashtable-set! cache x str)
|
|
|
|
str)))))
|
|
|
|
|
|
|
|
(define (draw prev-time)
|
2024-05-18 06:07:16 +00:00
|
|
|
(let ((player (level-player *level*)))
|
2024-04-26 18:30:59 +00:00
|
|
|
;; Draw background
|
|
|
|
(set-fill-color! context "#140c1c")
|
|
|
|
(fill-rect context 0.0 0.0 game-width game-height)
|
2024-05-18 06:07:16 +00:00
|
|
|
|
|
|
|
;; Draw player
|
2024-04-26 18:30:59 +00:00
|
|
|
(let ((w 22.0)
|
|
|
|
(h 22.0)
|
2024-05-18 06:07:16 +00:00
|
|
|
(position (player-position player)))
|
|
|
|
(draw-image context image:player
|
2024-04-26 18:30:59 +00:00
|
|
|
0.0 0.0 w h
|
2024-05-18 06:07:16 +00:00
|
|
|
(vec2-x position) (vec2-y position) w h)))
|
2024-04-26 18:30:59 +00:00
|
|
|
(request-animation-frame draw-callback))
|
|
|
|
(define draw-callback (procedure->external draw))
|
|
|
|
|
|
|
|
;; Input
|
|
|
|
(define key:left "ArrowLeft")
|
|
|
|
(define key:right "ArrowRight")
|
2024-05-18 04:31:31 +00:00
|
|
|
(define key:up "ArrowUp")
|
|
|
|
(define key:down "ArrowDown")
|
2024-04-26 18:30:59 +00:00
|
|
|
(define key:confirm "Enter")
|
|
|
|
|
|
|
|
(define (on-key-down event)
|
|
|
|
(let ((key (keyboard-event-code event)))
|
|
|
|
(match (level-state *level*)
|
|
|
|
('play
|
|
|
|
(cond
|
|
|
|
((string=? key key:left)
|
2024-05-18 06:07:16 +00:00
|
|
|
(set-level-move-left! *level* #t))
|
2024-04-26 18:30:59 +00:00
|
|
|
((string=? key key:right)
|
2024-05-18 06:07:16 +00:00
|
|
|
(set-level-move-right! *level* #t))
|
2024-05-18 04:31:31 +00:00
|
|
|
((string=? key key:up)
|
2024-05-18 06:07:16 +00:00
|
|
|
(set-level-move-up! *level* #t))
|
2024-05-18 04:31:31 +00:00
|
|
|
((string=? key key:down)
|
2024-05-18 06:07:16 +00:00
|
|
|
(set-level-move-down! *level* #t))))
|
2024-04-26 18:30:59 +00:00
|
|
|
((or 'win 'lose)
|
|
|
|
(when (string=? key key:confirm)
|
|
|
|
(set! *level* (make-level-1)))))))
|
|
|
|
|
|
|
|
(define (on-key-up event)
|
|
|
|
(let ((key (keyboard-event-code event)))
|
|
|
|
(match (level-state *level*)
|
|
|
|
('play
|
|
|
|
(cond
|
|
|
|
((string=? key key:left)
|
2024-05-18 06:07:16 +00:00
|
|
|
(set-level-move-left! *level* #f))
|
2024-04-26 18:30:59 +00:00
|
|
|
((string=? key key:right)
|
2024-05-18 06:07:16 +00:00
|
|
|
(set-level-move-right! *level* #f))
|
2024-05-18 04:31:31 +00:00
|
|
|
((string=? key key:up)
|
2024-05-18 06:07:16 +00:00
|
|
|
(set-level-move-up! *level* #f))
|
2024-05-18 04:31:31 +00:00
|
|
|
((string=? key key:down)
|
2024-05-18 06:07:16 +00:00
|
|
|
(set-level-move-down! *level* #f))))
|
2024-04-26 18:30:59 +00:00
|
|
|
(_ #t))))
|
|
|
|
|
|
|
|
;; Canvas and event loop setup
|
|
|
|
(define canvas (get-element-by-id "canvas"))
|
|
|
|
(define context (get-context canvas "2d"))
|
|
|
|
(set-element-width! canvas (exact game-width))
|
|
|
|
(set-element-height! canvas (exact game-height))
|
|
|
|
(add-event-listener! (current-document) "keydown"
|
|
|
|
(procedure->external on-key-down))
|
|
|
|
(add-event-listener! (current-document) "keyup"
|
|
|
|
(procedure->external on-key-up))
|
|
|
|
(request-animation-frame draw-callback)
|
|
|
|
(timeout update-callback dt)
|