autumn-lisp-game-jam-2024/game.scm

226 lines
8.4 KiB
Scheme
Raw Normal View History

;;; 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:
2024-10-27 13:34:16 +01:00
(use-modules (scheme base)
(dom document)
(dom element)
2024-10-27 13:34:16 +01:00
(dom event)
2024-10-28 17:22:51 +01:00
(dom window)
(math vector)
(hoot ffi)
2024-10-28 17:22:51 +01:00
(hoot debug)
(hoot hashtables)
2024-10-28 17:22:51 +01:00
(srfi srfi-9)
(ice-9 match))
2024-10-28 09:25:14 +01:00
(define *template* '())
(define (wrap-template template)
`(div (@ (id "container"))
(button (@ (click ,(lambda (event)
(set! *template* template-click)
(render))))
"Click")
(button (@ (click ,(lambda (event)
(set! *template* template-task)
(render))))
"Task")
2024-10-29 16:15:07 +01:00
(button (@ (click ,(lambda (event)
(set! *template* template-ascii-art)
(render))))
"Ascii art")
2024-10-28 09:25:14 +01:00
(div (@ (id "application")) ,(template))))
(define (render)
(let ((old (get-element-by-id "container")))
(unless (external-null? old) (remove! old))
(append-child! (document-body) (sxml->dom (wrap-template *template*)))))
2024-10-27 13:34:16 +01:00
(define (sxml->dom exp)
(match exp
;; The simple case: a string representing a text node.
((? string? str)
(make-text-node str))
;; An element tree. The first item is the HTML tag.
(((? symbol? tag) . body)
;; Create a new element with the given tag.
(let ((elem (make-element (symbol->string tag))))
(define (add-children children)
;; Recursively call sxml->dom for each child node and
;; append it to elem.
(for-each (lambda (child)
(append-child! elem (sxml->dom child)))
children))
(match body
;; '@' denotes an attribute list. Child nodes follow.
((('@ . attrs) . children)
;; Set attributes.
(for-each (lambda (attr)
(match attr
;; Attributes are (symbol string) tuples.
(((? symbol? name) (? string? val))
(set-attribute! elem
(symbol->string name)
val))
(((? symbol? name) (? boolean? val))
(set-attribute! elem
(symbol->string name)
val))
(((? symbol? name) (? procedure? proc))
(add-event-listener! elem
(symbol->string name)
(procedure->external proc)))))
attrs)
(add-children children))
;; No attributes, just a list of child nodes.
(children (add-children children)))
elem))))
;; Click
2024-10-28 09:25:14 +01:00
(define *clicks* 0)
2024-10-27 13:34:16 +01:00
(define (template-click)
`(div
(p ,(number->string *clicks*) " clicks")
(button (@ (click ,(lambda (event)
(set! *clicks* (+ *clicks* 1))
(render))))
"Click me!")))
;; Task
(define-record-type <task>
(make-task name done?)
task?
(name task-name)
(done? task-done? set-task-done!))
(define *tasks* '())
(define (add-task! task)
(set! *tasks* (cons task *tasks*)))
(define (remove-task! task)
(set! *tasks* (delq task *tasks*)))
(define (template-task)
(define (task-template task)
`(li (input (@ (type "checkbox")
(change ,(lambda (event)
(let* ((checkbox (event-target event))
(checked? (element-checked? checkbox)))
(set-task-done! task checked?)
(render))))
(checked ,(task-done? task))))
(span (@ (style "padding: 0 1em 0 1em;"))
,(if (task-done? task)
`(s ,(task-name task))
(task-name task)))
(a (@ (href "#")
(click ,(lambda (event)
(remove-task! task)
(render))))
"remove")))
`(div
(h2 "Tasks")
;; Tasks are stored in reverse order.
(ul ,@(map task-template (reverse *tasks*)))
(input (@ (id "new-task")
(placeholder "Write more task")))
;; Add new task on click
(button (@ (click ,(lambda (event)
(let* ((input (get-element-by-id "new-task"))
(name (element-value input)))
(unless (string=? name "")
(add-task! (make-task name #f))
(set-element-value! input "")
(render))))))
"Add task")))
2024-10-29 16:15:07 +01:00
;; render ascii art
(define (template-ascii-art)
(define ink-script '(
"(\\ "
"\\'\\ "
" \\'\\ __________ "
" / '| ()_________) "
" \\ '/ \\ ~~~~~~~~ \\ "
" \\ \\ ~~~~~~ \\ "
" ==). \\__________\\ "
" (__) ()__________) "
))
`(pre ,(string-join ink-script "\n")))
(define (sub-string-list string-list start len)
(define (sub-string-list-iter string-list start len part)
(if (string? (car string-list))
(if (> (string-length (car string-list)) start)
(append part
(if (= start 0 )
'()
(substring (car string-list) 0 start))
(list (substring (car string-list) start (+ start len)))
(if (= (+ start len) (string-length (car string-list)))
'()
(list (substring (car string-list) (+ start len) (string-length (car string-list)))))
(cdr string-list))
(sub-string-list-iter (cdr string-list)
(- start (string-length (car string-list)))
len
(append part (list (car string-list)))))
(sub-string-list-iter (cdr string-list)
(- start (string-length (car (last-pair (car string-list)))))
len
(append part (list (car string-list))))))
(sub-string-list-iter string-list start len '()))
2024-10-31 16:40:26 +01:00
2024-10-27 13:34:16 +01:00
;; Main
(set! *template* template-task)
2024-10-28 17:22:51 +01:00
(define *update-list* '())
(define dt (/ 1000.0 60.0))
(define-record-type <timeout-function>
(make-timeout-function interval countdown func)
timeout-function?
(interval timeout-function-interval set-timeout-function-interval!)
(countdown timeout-function-countdown set-timeout-function-countdown!)
(func timeout-function-func))
(define (plus-clicks)
(set! *clicks* (+ *clicks* 1)))
(set! *update-list* (append *update-list* (list (make-timeout-function 60 60 plus-clicks))))
(define (update)
(for-each (lambda (i)
(dprint "countdown" (timeout-function-countdown i))
(if (= (timeout-function-countdown i) 0)
(begin
((timeout-function-func i))
(set-timeout-function-countdown! i (timeout-function-interval i))
(if (equal? *template* template-click)
(render)))
2024-10-28 17:22:51 +01:00
(set-timeout-function-countdown! i (- (timeout-function-countdown i) 1))))
*update-list*)
(timeout update-callback dt))
(define update-callback (procedure->external update))
2024-10-27 13:34:16 +01:00
(render)
2024-10-28 17:22:51 +01:00
(timeout update-callback dt)