Features:
- One monster type: Zombies!
- Teleporters
- Artificial Unintelligence (tm) technology for realistic zombie horde
behavior
- Full in-game documentation
- Trees
- Gold (because really, what's a roguelike without gold?)
- Each game is guaranteed to end in death by zombie
Release Notes:
- Designed for use with CLISP. Any other Lisp is likely not to work
without modification, due to implementation-specific single-character
input code. If you're trying to alter this program for your Lisp, the
function "get-input-char" is the only thing you need to touch.
- To run the program:
- install CLISP
- save the code as "zombies.lisp"
- type "clisp zombies.lisp" and start running for your life!
- The program is designed to run in an 80x24 terminal. Bigger will
work OK, smaller definitely won't.
License:
- This software is released under the Zombie Public License (ZPL).
This means you can use the code for anything you like, but if you
complain about any aspect of it, zombies will eat your BRRRAAAAINS!!!
Partial screenshot:
Level: 2 HP: 17 Gold: 10
.......#....#..$.......#..Z...
....................$.........
..............#Z....#.....#Z..
........#...>.Z.....ZZZ.......
...#......@Z...#Z.#..Z.ZZ.....
..........$...........#.......
..............................
On with the code:
(setf *random-state* (make-random-state t))
(defparameter *maxx* 79)
(defparameter *maxy* 22)
(defstruct player
x
y
(hp 20)
(gold 0))
(defstruct zombie
x
y)
(defvar *screen* (make-array (list *maxx* *maxy*)))
(defvar *messages* ())
(defvar *level* 0)
(defvar *player* (make-player))
(defvar *zombies* ())
(defun get-input-char ()
(system::input-character-char
(EXT:with-keyboard (read-char EXT:*keyboard-input*))))
(defun play-game()
(dotimes (i (1+ *maxy*))
(format t "~%"))
(new-level)
(setf *messages* (cons "Zombies! Press ? for help." nil))
(print-screen)
(loop
(setf *messages* nil)
(when (process-player)
(recover-hp)
(process-zombies)
(if (< (player-hp *player*) 1)
(die "You've been killed by zombies."))
(print-screen))))
(defun new-level ()
(dotimes (i (* 2 *maxy*))
(format t "~%"))
(incf *level*)
(dotimes (i *maxy*)
(dotimes (j *maxx*)
(setf (aref *screen* j i) (list #\.))))
(dotimes (i (random 100))
(push #\$ (aref *screen* (random *maxx*) (random *maxy*))))
(dotimes (i (random 10))
(push #\^ (aref *screen* (random *maxx*) (random *maxy*))))
(dotimes (i (random 80))
(push #\# (aref *screen* (random *maxx*) (random *maxy*))))
(setf *zombies* nil)
(dotimes (i (+ 8 (* 2 *level*)))
(let ((x (random *maxx*))
(y (random *maxy*)))
(push (make-zombie :x x :y y) *zombies*)
(push #\Z (aref *screen* x y))))
(push #\> (aref *screen* (random *maxx*) (random *maxy*)))
(let ((x (random *maxx*))
(y (random *maxy*)))
(setf (player-x *player*) x)
(setf (player-y *player*) y)
(push #\@ (aref *screen* x y)))
(push (format nil "Welcome to Level ~A." *level*) *messages*))
(defun print-screen ()
(format t "~A" #\Return)
(dotimes (i (1+ *maxx*))
(format t " "))
(format t "~A[23A" (code-char 27))
(format t "~A" #\Return)
(dotimes (i (1+ *maxx*))
(format t " "))
(format t "~A" #\Return)
(format t "Level: ~A HP: ~A Gold: ~A"
*level* (player-hp *player*) (player-gold *player*))
(dotimes (i *maxy*)
(format t "~%")
(dotimes (j *maxx*)
(format t "~A" (car (aref *screen* j i)))))
(format t "~%")
(dolist (i (reverse *messages*))
(format t "~A " i)))
(defun process-player ()
(let ((input (get-input-char))
(new-x nil)
(new-y nil))
(cond ((equal input #\q) (die "Goodbye."))
((equal input #\h)
(setf new-x (- (player-x *player*) 1))
(setf new-y (player-y *player*)))
((equal input #\j)
(setf new-y (+ (player-y *player*) 1))
(setf new-x (player-x *player*)))
((equal input #\k)
(setf new-y (- (player-y *player*) 1))
(setf new-x (player-x *player*)))
((equal input #\l)
(setf new-x (+ (player-x *player*) 1))
(setf new-y (player-y *player*)))
((equal input #\y)
(setf new-x (- (player-x *player*) 1))
(setf new-y (- (player-y *player*) 1)))
((equal input #\u)
(setf new-x (+ (player-x *player*) 1))
(setf new-y (- (player-y *player*) 1)))
((equal input #\b)
(setf new-x (- (player-x *player*) 1))
(setf new-y (+ (player-y *player*) 1)))
((equal input #\n)
(setf new-x (+ (player-x *player*) 1))
(setf new-y (+ (player-y *player*) 1)))
((equal input #\.)
(setf new-x (player-x *player*))
(setf new-y (player-y *player*)))
((equal input #\?) (print-help) (return-from process-player nil))
(t (return-from process-player nil)))
(when (or (< new-x 0) (>= new-x *maxx*) (< new-y 0) (>= new-y
*maxy*))
(return-from process-player nil))
(when (equal (car (aref *screen* new-x new-y)) #\>)
(new-level)
(return-from process-player t))
(let ((gold 0))
(loop
(if (not (equal (car (aref *screen* new-x new-y)) #\$))
(return))
(incf (player-gold *player*))
(pop (aref *screen* new-x new-y))
(incf gold))
(if (> gold 0)
(push (format nil "Found ~A gold." gold) *messages*)))
(when (equal (car (aref *screen* new-x new-y)) #\Z)
(return-from process-player nil))
(when (equal (car (aref *screen* new-x new-y)) #\#)
(return-from process-player nil))
(when (equal (car (aref *screen* new-x new-y)) #\^)
(pop (aref *screen* new-x new-y))
(loop
(setf new-x (random *maxx*))
(setf new-y (random *maxy*))
(if (equal (car (aref *screen* new-x new-y)) #\.)
(return))))
(move-player new-x new-y)))
(defun move-player (x y)
(pop (aref *screen* (player-x *player*) (player-y *player*)))
(setf (player-x *player*) x)
(setf (player-y *player*) y)
(push #\@ (aref *screen* x y)))
(defun recover-hp ()
(if (and (= 0 (random 10)) (< (player-hp *player*) 20))
(incf (player-hp *player*))))
(defun process-zombies ()
(dotimes (z (length *zombies*))
(if (or (= 0 (random 7)) (not (process-zombie-smart z)))
(process-zombie-random z))))
(defun process-zombie-smart (z)
(let* ((z-x (zombie-x (nth z *zombies*)))
(z-y (zombie-y (nth z *zombies*)))
(p-x (player-x *player*))
(p-y (player-y *player*))
(new-x z-x)
(new-y z-y))
(cond ((> z-x p-x) (decf new-x))
((< z-x p-x) (incf new-x)))
(cond ((> z-y p-y) (decf new-y))
((< z-y p-y) (incf new-y)))
(when (equal (car (aref *screen* new-x new-y)) #\@)
(decf (player-hp *player*))
(push "Ouch!" *messages*)
(return-from process-zombie-smart t))
(when (equal (car (aref *screen* new-x new-y)) #\Z)
(return-from process-zombie-smart nil))
(when (equal (car (aref *screen* new-x new-y)) #\#)
(return-from process-zombie-smart nil))
(when (equal (car (aref *screen* new-x new-y)) #\^)
(loop
(setf new-x (random *maxx*))
(setf new-y (random *maxy*))
(if (equal (car (aref *screen* new-x new-y)) #\.)
(return))))
(move-zombie z new-x new-y)
t))
(defun process-zombie-random (z)
(let* ((z-x (zombie-x (nth z *zombies*)))
(z-y (zombie-y (nth z *zombies*)))
(new-x z-x)
(new-y z-y)
(rand-x (random 3))
(rand-y (random 3)))
(if (= rand-x 0)
(incf new-x)
(if (= rand-x 1)
(decf new-x)))
(if (= rand-y 0)
(incf new-y)
(if (= rand-y 1)
(decf new-y)))
(when (or (< new-x 0) (>= new-x *maxx*) (< new-y 0) (>= new-y
*maxy*))
(return-from process-zombie-random nil))
(when (equal (car (aref *screen* new-x new-y)) #\Z)
(return-from process-zombie-random nil))
(when (equal (car (aref *screen* new-x new-y)) #\#)
(return-from process-zombie-random nil))
(when (equal (car (aref *screen* new-x new-y)) #\^)
(loop
(setf new-x (random *maxx*))
(setf new-y (random *maxy*))
(if (equal (car (aref *screen* new-x new-y)) #\.)
(return))))
(move-zombie z new-x new-y)))
(defun move-zombie (z x y)
(pop (aref *screen*
(zombie-x (nth z *zombies*))
(zombie-y (nth z *zombies*))))
(setf (zombie-x (nth z *zombies*)) x)
(setf (zombie-y (nth z *zombies*)) y)
(push #\Z (aref *screen* x y)))
(defun die (message)
(format t "~%~%~A Score: ~A~%~%" message
(+ (* (player-gold *player*) *level*) (* 10 (- *level* 1))))
(quit))
(defun print-help ()
(dotimes (i (+ 3 *maxx*))
(format t "~%"))
(format t "Welcome to Zombies!~%~%")
(format t "The world has been overrun by zombies. ")
(format t "You have no weapons or other means of~%")
(format t "self-defense. All you can do is run for ")
(format t "the exit! That, and collect gold.~%~%")
(format t "Objects:~%@ - You~%Z - Zombies!~%$ - Gold")
(format t "~%# - Trees~%^ - Teleporters~%")
(format t "> - Level Exit~%~%Controls:~%y k u~%h @ l")
(format t "~%b j n~%~%. - Wait~%q - Quit~%~%")
(format t "Press any key to continue.~%")
(get-input-char)
(print-screen))
(play-game)
>Simultaneously announcing plans for, and completion of, Zombies!
>Zombies! is a roguelike written entirely from scratch, in one day, in
>Common Lisp.
>
>Features:
>- One monster type: Zombies!
>- Teleporters
>- Artificial Unintelligence (tm) technology for realistic zombie horde
>behavior
>- Full in-game documentation
>- Trees
>- Gold (because really, what's a roguelike without gold?)
>- Each game is guaranteed to end in death by zombie
That's excellent! I never managed to find a way to make a roguelike in
CL. The last idea that I tried to do was to make a C application that
works as a console and also as a server, to which Lisp application would
connect. Unfortunately using socket library in C is a very non-trivial
task and I couldn't estabilish even a simple server.
--
|WAR HAS NEVER SOLVED ANYTHING|,----- Timofei Shatrov aka Grue---------.
|(except for ending slavery, ||mail: grue at mail.ru ================ |
| fascism and communism) ||============= http://grue3.tripod.com |
|...and Saddam's dictatorship |`----------------------------------[4*72]
--
Slash
If you mean "windows executable", not really. It's written with
CLISP-specific IO code, and there's no native Windows port of CLISP.
Making a Windows port would likely push it out of the 1DRL category.
If you mean "UNIX executable", just install CLISP, then add this first
line to the file and mark it executable ;)
#!/usr/bin/clisp
>
>Slash wrote:
>> micr...@gmail.com wrote:
>> > Simultaneously announcing plans for, and completion of, Zombies!
>> > Zombies! is a roguelike written entirely from scratch, in one day, in
>> > Common Lisp.
>> >
>> Any chance for an executable?
>>
>> --
>> Slash
>
>
>If you mean "windows executable", not really. It's written with
>CLISP-specific IO code, and there's no native Windows port of CLISP.
There is one (always was)! And it even produces executables. I can make
one for you if you want.
Yes, please. I am having a look at some of these projects, interesting
to see what people can create in a short while. But compiling from
source is just silly for me... Am I expected to have windows ports of
GNU compilers just to try out little games...
I don't see why people would provide source-only in the first place.
It's just an unnecessary inconvenience.
--
Kostatus (kostatus001 at ihug co nz)
[flamewar warning]
> Yes, please. I am having a look at some of these projects, interesting
> to see what people can create in a short while. But compiling from
> source is just silly for me... Am I expected to have windows ports of
> GNU compilers just to try out little games...
>
> I don't see why people would provide source-only in the first place.
> It's just an unnecessary inconvenience.
I could say the same about windows binaries. Running compiled exe files is
just silly for me... Am I expected to have linux ports of windows
libraries and utilities just to try out little games...
I don't see why people would provide binary-only in the first place.
It's just an unnecessary inconvenience.
(especially, when the game is written in an interpreted language)
--
Radomir `The Sheep' Dopieralski
That's wrong! Lisp has been compiled language for at least 20 years...
Anyway, you can get zombies binary at
http://rapidshare.de/files/14913710/zombies.zip.html
Original author, or anyone else may upload it to more convenient place.
It has to be recognized that source distribution allows for higher
flexibility and persistence of the program over the time, but binary
distributions are necessary for people who don't have access to the
compiler or just cant afford to spend time compiling it just to run it
and give it a look.
>
> (especially, when the game is written in an interpreted language)
>
> --
> Radomir `The Sheep' Dopieralski
--
Slash
Wow, I don't know how I entirely missed that Windows port of CLISP. I
need to pay more attention. Thanks!
I altered the program slightly to handle Windows' terminal
characteristics better, and compiled a new binary (using CLISP's
excellent facilities, thanks again). Here you go, just click n' play:
http://www.storeandserve.com/download/130366/zombies-win.zip.html
...
> I could say the same about windows binaries. Running compiled exe files is
> just silly for me... Am I expected to have linux ports of windows
> libraries and utilities just to try out little games...
Are you telling me Linux doesn't have binaries?
>
> I don't see why people would provide binary-only in the first place.
> It's just an unnecessary inconvenience.
Binary -> double-click and run.
Source -> compile (takes time) -> double-click and run.
The compilation step is a waste of time as it does not add to the game
in any way apart from making it harder to run.
>
> (especially, when the game is written in an interpreted language)
>
--
(And stop assuming that all Linux platforms are Intel x86 without saying
so, though I don't think anyone from this groups is guilty of that.)
--
Simon Richard Clarkstone: s.r.cl?rkst?n?@durham.ac.uk/s?m?n.cl?rkst?n?@
hotmail.com ### "I have a spelling chequer / it came with my PC /
it plainly marks for my revue / Mistake's I cannot sea" ...
by: John Brophy (at: http://www.cfwf.ca/farmj/fjjun96/)