;;; ;;; rendering_1.lisp ;;; ;;; Sample implementation of the first rendering model ;;; for the Dungeon Crawl Project. ;;; ;;; Alastair Bridgewater, October 9th, 2004. ;;; ;;; Damaged again in may 2009. ;;; (defpackage :rendering-1 (:use :common-lisp) (:export #:start-example)) ;(require :clx) (in-package :rendering-1) (defparameter *raw-map-data* '("xxxxxxxxxxxxxxxx" "x.xxxxxxxxx...xx" "x....x.....xx..x" "x.xx...xxx.xxx.x" "x.xxxxxxxx...x.x" "x......xxxxx.x.x" "xxxxxx.xxxxx.x.x" "xxxxx....xx..x.x" "xxxxx.xx.xx.x..x" "xx....xxxxx.x.xx" "xx.xx...xxx.x..x" "xx.xxxx...x.xx.x" "xx.xxxxxx...xx.x" "xx.xx....xxx...x" "xx....xx.....xxx" "xxxxxxxxxxxxxxxx") "The raw map data, in easily editable form.") (declaim (type (simple-array t (#x100)) *map-data*)) (defvar *map-data* (make-array #x100) "Map data. Each cell is either T for a wall or NIL for empty space.") (defvar *position* #x11 "Position of player within *map-data*.") (defvar *facing* :south "Direction player is facing.") (defvar *frontstep* 0) (defvar *leftstep* 0) (defparameter *frontstep-list* '(:north -16 :south 16 :west -1 :east 1) "alist from directions to index change within map data to move forward.") (defparameter *leftstep-list* '(:north -1 :south 1 :west 16 :east -16) "alist from directions to index change within map data to move left.") (defparameter *leftturn-list* '(:north :west :west :south :south :east :east :north) "alist from direction to direction for turning left.") (defparameter *rightturn-list* '(:north :east :east :south :south :west :west :north) "alist from direction to direction for turning right.") (defvar *display* nil "The X display connection.") (defvar *window* nil "The X window we draw in.") (defvar *context* nil "The X graphics context we draw with.") ;; For some reason, CLX doesn't appear to have these keysyms defined. (defconstant +xk-up+ #xff52) (defconstant +xk-left+ #xff51) (defconstant +xk-right+ #xff53) (defun init-map-data () "Convert the raw map data in *raw-map-data* to the internal representation in *map-data*." (let ((row-number 0)) (dolist (row-data *raw-map-data*) (dotimes (i 16) (setf (aref *map-data* (+ i (* 16 row-number))) (char= (aref row-data i) #\x))) (incf row-number))) (values)) (defun set-facing (direction) "Set the player to be facing in DIRECTION. Sets up *frontstep* and *leftstep* for rendering and motion control." (setf *facing* direction) (setf *frontstep* (getf *frontstep-list* direction)) (setf *leftstep* (getf *leftstep-list* direction))) (defun turn-left () "Turn the player 90 degrees to the left." (set-facing (getf *leftturn-list* *facing*))) (defun turn-right () "Turn the player 90 degrees to the right." (set-facing (getf *rightturn-list* *facing*))) (defun move-forward () "Move the player one space forward if there is no wall ahead." (if (not (aref *map-data* (+ *position* *frontstep*))) (setf *position* (+ *position* *frontstep*)))) (defun draw-line (x1 y1 x2 y2) (xlib:draw-line *window* *context* x1 y1 x2 y2)) (defun draw-left-side (position base size) (if (aref *map-data* (+ position *leftstep*)) (progn ;; There is a wall to the left of this position, so we draw it. (draw-line base base (+ base size) (+ base size)) (draw-line base (- 255 base) (+ base size) (- 255 base size))) (progn ;; There is no wall to the left of this position, so there is one ;; ahead of it. We draw that one. (draw-line base (+ base size) (+ base size) (+ base size)) (draw-line base (- 255 base size) (+ base size) (- 255 base size)))) ;; Draw the vertical line for this wall segment. (draw-line (+ base size) (+ base size) (+ base size) (- 255 base size))) (defun draw-right-side (position base size) (if (aref *map-data* (- position *leftstep*)) (progn ;; There is a wall to the right of this position, so we draw it. (draw-line (- 255 base) base (- 255 base size) (+ base size)) (draw-line (- 255 base) (- 255 base) (- 255 base size) (- 255 base size))) (progn ;; There is no wall to the right of this position, so there is one ;; ahead of it. We draw that one. (draw-line (- 255 base) (+ base size) (- 255 base size) (+ base size)) (draw-line (- 255 base) (- 255 base size) (- 255 base size) (- 255 base size)))) ;; Draw the vertical line for this wall segment. (draw-line (- 255 base size) (+ base size) (- 255 base size) (- 255 base size))) (defun draw-maze () "Draw the maze as seen from the player's current position and facing." (let ((base 0) (position *position*)) (dotimes (depth 4) ;; size values determined empirically. (let ((size (elt '(10 50 40 15) depth))) (draw-left-side position base size) (draw-right-side position base size) (incf position *frontstep*) (incf base size) ;; Draw the facing wall if there is one. (when (aref *map-data* position) (draw-line base base (- 255 base) base) (draw-line base (- 255 base) (- 255 base) (- 255 base)) (return-from draw-maze))))) (values)) (defun force-redraw () (xlib:clear-area *window* :x 0 :y 0 :width 256 :height 256 :exposures-p t)) (defun create-gc (screen) (setf *context* (xlib:create-gcontext :foreground (xlib:screen-black-pixel screen) :background (xlib:screen-white-pixel screen) :line-width 1 :cap-style :projecting :drawable (xlib:screen-root screen)))) (defun init-display () (setf *display* (xlib:open-default-display)) (let ((screen (xlib:display-default-screen *display*))) (create-gc screen) (setf *window* (xlib:create-window :parent (xlib:screen-root screen) :x 0 :y 0 :width 256 :height 256 :background (xlib:screen-white-pixel screen)))) (setf (xlib:window-event-mask *window*) (xlib:make-event-mask :button-press :button-release :exposure :key-press)) (setf (xlib:wm-name *window*) "Dungeon Crawl -- Rendering 1") (setf (xlib:wm-normal-hints *window*) (xlib:make-wm-size-hints :width 256 :height 256 :min-width 256 :min-height 256 :max-width 256 :max-height 256)) (xlib:map-window *window*)) (defun close-display () (setf *window* nil) (setf *context* nil) (xlib:close-display *display*) (setf *display* nil)) (defun run-event-loop () (xlib:event-case (*display*) (:exposure () (draw-maze) nil) (:button-release () t) (:key-press (code) (let ((keysym (xlib:keycode->keysym *display* code 0))) (declare (integer keysym)) (cond ;; For some reason, the keysyms I need aren't defined in CLX. ((= keysym +xk-left+) (turn-left) (force-redraw)) ((= keysym +xk-right+) (turn-right) (force-redraw)) ((= keysym +xk-up+) (move-forward) (force-redraw)))) nil))) (defun start-example () "run the example renderer, connecting to an X display on HOST." (init-map-data) (setf *position* #x11) (set-facing :south) (init-display) (run-event-loop) (close-display) (values)) ;;; EOF