-
Notifications
You must be signed in to change notification settings - Fork 3
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
initial commit with full tutorial code
- Loading branch information
Showing
18 changed files
with
8,116 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
# Backup files | ||
*~ |
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,19 @@ | ||
# Complete roguelike tutorial with libtcod and cl-tcod | ||
|
||
## Introduction | ||
|
||
This is a port of the Python code from the ["Complete Roguelike Tutorial, using python+libtcod"](http://www.roguebasin.com/index.php?title=Complete_Roguelike_Tutorial,_using_python%2Blibtcod) into Common Lisp using [cl-tcod](https://bitbucket.org/eeeickythump/cl-tcod). It uses [libtcod](https://bitbucket.org/libtcod/libtcod) (a.k.a. "The Doryen Library") through the Common Foreign Function Interface, [CFFI](https://common-lisp.net/project/cffi/). Commentary on writing this tutorial in lisp will be found on my [blog](https://9bladed.com). | ||
|
||
The code is basically a straight "translation" from Python to Common Lisp, and is not necessarily "lispy" in it's current form. This was done on purpose to make it easy to refer to the original tutorial, and to reduce the chance of bugs in making sure this works as a proof of concept of a roguelike in lisp. | ||
|
||
Everything works exactly the same in terms of gameplay and presentation, as near as I can tell. The only exception is that currently cannot restore savegames where a monster was confused (via a "scroll of confusion"). This is due to the use of a recursive anonymous function (lambda). There are probably several ways around this, but for the purposes of this tutorial translation this is a minor issue and will be left as is for now. | ||
|
||
## Technical Details | ||
|
||
cl-tcod is in the process of being updated to fix some bugs with the latest version of libtcod. Details here and on the blog posts will discuss the changes necessary if an updated version of cl-tcod is not on the official repository or mirrored on GitHub. | ||
|
||
This code has only been tested with the current versions of libtcod (1.6.2), SBCL (1.3.12), and SDL (2.0.5) on x86_64 Arch Linux. | ||
|
||
## Status | ||
|
||
All of the code works (other than the saving caveat mentioned above), but is missing some of the original Python comments and lisp specific comments. The code is not necessarily pretty, well organized, or in a final state. Updates will be made as the blog posts go up for each part. It is possible in the future to produce a "lisp first" approach to the tutorial, but that is not planned yet. |
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,45 @@ | ||
;;;; tutorial-part01.lisp | ||
;;;; "Graphics" | ||
;;;; February 2017 | ||
;;;; | ||
;;;; Commentary to be posted on 9bladed.com | ||
;;;; | ||
;;;; Original python+libtcod tutorial, part 1: | ||
;;;; http://www.roguebasin.com/index.php?title=Complete_Roguelike_Tutorial,_using_python%2Blibtcod,_part_1 | ||
|
||
(ql:quickload :tcod) | ||
|
||
(defparameter *screen-width* 80) | ||
(defparameter *screen-height* 50) | ||
(defparameter *limit-fps* 20) | ||
|
||
(tcod:console-set-custom-font "arial10x10.png" '(:FONT-TYPE-GREYSCALE :FONT-LAYOUT-TCOD)) | ||
|
||
(tcod:console-init-root *screen-width* *screen-height* :title "lisp/libtcod tutorial" :fullscreen? nil) | ||
|
||
(tcod:sys-set-fps *limit-fps*) | ||
|
||
(defvar *playerx* (/ *screen-width* 2)) | ||
(defvar *playery* (/ *screen-height* 2)) | ||
|
||
(defun handle-keys () | ||
(let ((events (tcod:sys-get-events))) | ||
(loop for event in events | ||
when (eql (car event) :event-key-press) | ||
do (cond ((and (eql (tcod:key-vk (cdr event)) :ENTER) (tcod:key-lalt (cdr event))) | ||
(tcod:console-set-fullscreen (not (tcod:console-is-fullscreen?)))) | ||
((eql (tcod:key-c (cdr event)) #\f) | ||
(tcod:console-set-fullscreen (not (tcod:console-is-fullscreen?)))) | ||
((eql (tcod:key-vk (cdr event)) :UP) (decf *playery*)) | ||
((eql (tcod:key-vk (cdr event)) :DOWN) (incf *playery*)) | ||
((eql (tcod:key-vk (cdr event)) :LEFT) (decf *playerx*)) | ||
((eql (tcod:key-vk (cdr event)) :RIGHT) (incf *playerx*)) | ||
((eql (tcod:key-vk (cdr event)) :ESCAPE) (return-from handle-keys :exit)))))) | ||
|
||
(do () | ||
((tcod:console-is-window-closed?)) | ||
(tcod:console-set-default-foreground tcod:*root* (tcod:color :white)) | ||
(tcod:console-put-char tcod:*root* *playerx* *playery* (char-code #\@) :NONE) | ||
(tcod:console-flush) | ||
(tcod:console-put-char tcod:*root* *playerx* *playery* (char-code #\SPACE) :NONE) | ||
(handle-keys)) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,151 @@ | ||
;;;; tutorial-part02.lisp | ||
;;;; "The object and the map" | ||
;;;; February 2017 | ||
;;;; | ||
;;;; Commentary to be posted on 9bladed.com | ||
;;;; | ||
;;;; Original python+libtcod tutorial, part 2: | ||
;;;; http://www.roguebasin.com/index.php?title=Complete_Roguelike_Tutorial,_using_python%2Blibtcod,_part_2 | ||
|
||
(ql:quickload :tcod) | ||
|
||
(defparameter *screen-width* 80) | ||
(defparameter *screen-height* 50) | ||
(defparameter *limit-fps* 20) | ||
|
||
(defparameter *map-width* 80) | ||
(defparameter *map-height* 45) | ||
|
||
(tcod:make-color :dark-wall 0 0 100) | ||
(tcod:make-color :dark-ground 50 50 150) | ||
|
||
(tcod:console-set-custom-font "arial10x10.png" '(:FONT-TYPE-GREYSCALE :FONT-LAYOUT-TCOD)) | ||
|
||
(tcod:console-init-root *screen-width* *screen-height* | ||
:title "lisp/libtcod tutorial" :fullscreen? nil :renderer :renderer-sdl) | ||
(defparameter *con* (tcod:console-new *screen-width* *screen-height*)) | ||
|
||
(tcod:sys-set-fps *limit-fps*) | ||
|
||
(defgeneric move (obj dx dy) | ||
(:documentation "move by the given amount")) | ||
|
||
(defgeneric draw (obj) | ||
(:documentation "set the color and then draw the character that represents this object at its position")) | ||
|
||
(defgeneric clear (obj) | ||
(:documentation "erase the character that represents this object")) | ||
|
||
(defclass tile () | ||
((blocked | ||
:initarg :blocked | ||
:initform (error "Must specify blocked") | ||
:accessor blocked) | ||
(block-sight | ||
:initarg :block-sight | ||
:initform nil | ||
:accessor block-sight)) | ||
(:documentation "a tile of the map and its properties")) | ||
|
||
(defmethod initialize-instance :after ((tt tile) &key) | ||
"by default, if a tile is blocked, it also blocks sight" | ||
(if (not (block-sight tt)) | ||
(setf (block-sight tt) (blocked tt)))) | ||
|
||
;; (defmethod draw ((tt tile) &key x y) | ||
;; (if (block-sight tt) | ||
;; (tcod:console-set-default-background *con* x y :dark-wall :set) | ||
;; (tcod:console-set-default-background *con* x y :dark-ground :set))) | ||
|
||
(defvar *map*) | ||
(defun make-map () | ||
(setf *map* (make-array (list *map-height* *map-width*))) | ||
(dotimes (i *map-height*) | ||
(dotimes (j *map-width*) | ||
(setf (aref *map* i j) (make-instance 'tile :blocked nil)))) | ||
|
||
(setf (blocked (aref *map* 22 30)) t) | ||
(setf (block-sight (aref *map* 22 30)) t) | ||
(setf (blocked (aref *map* 22 50)) t) | ||
(setf (block-sight (aref *map* 22 50)) t)) | ||
|
||
(defclass object () | ||
((x | ||
:initarg :x | ||
:initform (error "Must have a x value") | ||
:accessor x) | ||
(y | ||
:initarg :y | ||
:initform (error "Must have a y value") | ||
:accessor y) | ||
(cha | ||
:initarg :cha | ||
:initform (error "Must have a character") | ||
:accessor cha) | ||
(color | ||
:initarg :color | ||
:initform (error "Must have a color") | ||
:accessor color)) | ||
(:documentation "this is a generic object: the player, a monster, an item, the stairs... | ||
it's always represented by a character on the screen.")) | ||
|
||
(defmethod move ((obj object) dx dy) | ||
(if (not (blocked (aref *map* (+ (y obj) dy) (+ (x obj) dx)))) | ||
(progn | ||
(incf (x obj) dx) | ||
(incf (y obj) dy)))) | ||
|
||
(defmethod draw ((obj object)) | ||
(tcod:console-set-default-foreground *con* (tcod:color (color obj))) | ||
(tcod:console-put-char *con* (x obj) (y obj) (char-code (cha obj)) :NONE)) | ||
|
||
(defmethod clear ((obj object)) | ||
(tcod:console-put-char *con* (x obj) (y obj) (char-code #\SPACE) :NONE)) | ||
|
||
(defun handle-keys () | ||
(let ((events (tcod:sys-get-events))) | ||
(loop for event in events | ||
when (eql (car event) :event-key-press) | ||
do (pprint (cdr event)) | ||
(cond ((or (eql (tcod:key-vk (cdr event)) :ENTER) (tcod:key-lctrl (cdr event))) | ||
(print (tcod:key-lctrl (cdr event))) | ||
(tcod:console-set-fullscreen (not (tcod:console-is-fullscreen?)))) | ||
((eql (tcod:key-c (cdr event)) #\f) | ||
(pprint (tcod:key-c (cdr event)))) | ||
((eql (tcod:key-vk (cdr event)) :UP) (move *player* 0 -1)) | ||
((eql (tcod:key-vk (cdr event)) :DOWN) (move *player* 0 1)) | ||
((eql (tcod:key-vk (cdr event)) :LEFT) (move *player* -1 0)) | ||
((eql (tcod:key-vk (cdr event)) :RIGHT) (move *player* 1 0)) | ||
((eql (tcod:key-vk (cdr event)) :ESCAPE) (return-from handle-keys :exit)))))) | ||
|
||
(defparameter *player* (make-instance 'object | ||
:x (/ *screen-width* 2) :y (/ *screen-height* 2) | ||
:cha #\@ :color :white)) | ||
(defparameter *npc* (make-instance 'object | ||
:x (- (/ *screen-width* 2) 5) :y (/ *screen-height* 2) | ||
:cha #\@ :color :yellow)) | ||
(defparameter *objects* (list *player* *npc*)) | ||
|
||
;; generate map (at this point it's not drawn to the screen) | ||
(make-map) | ||
|
||
(defun render-all () | ||
;; go through all the tiles, and set their background color | ||
(dotimes (i *map-height*) | ||
(dotimes (j *map-width*) | ||
(if (block-sight (aref *map* i j)) | ||
(tcod:console-set-char-background *con* j i (tcod:color :dark-wall) :set) | ||
(tcod:console-set-char-background *con* j i (tcod:color :dark-ground) :set)))) | ||
|
||
;; draw all objects in the list | ||
(mapcar #'draw *objects*) | ||
|
||
;; blit the contents of con to the root console | ||
(tcod:console-blit *con* 0 0 *screen-width* *screen-height* tcod:*root* 0 0 1.0 1.0)) | ||
|
||
(do () | ||
((tcod:console-is-window-closed?)) | ||
(render-all) | ||
(tcod:console-flush) | ||
(mapcar #'clear *objects*) | ||
(handle-keys)) |
Oops, something went wrong.