diff --git a/biome.lisp b/biome.lisp deleted file mode 100644 index fde3c3e..0000000 --- a/biome.lisp +++ /dev/null @@ -1,65 +0,0 @@ -;;;; -;;;; Terra Nostra is a Minecraft-like survival game for the commandline. -;;;; -;;;; Biomes are habitat types, that provide default functions for patches. -;;;; This file defines the biome struct and the inbuilt biomes. -;;;; -;;;; (c) 2018 Daniel Vedder, MIT license -;;;; - -;; Biome struct and register - -(defstruct biome - (name "") - (ground "") - (occupants '()) ;an alist of possible occupants and their probabilities - (char #\.) ;default map display character - (col 'white)) ;default map display colour - - -(let ((biome-list NIL)) - (defun register-biome (symbol-name biome-object) - (setf biome-list (cons (list symbol-name biome-object) biome-list))) - - (defun available-biomes () - (keys biome-list)) - - (defun get-biome (symbol-name) - (cassoc symbol-name biome-list))) - -(defmacro new-biome (name &body body) - `(register-biome ,name - (make-biome ,@body))) - - -;; Biome definitions - -(new-biome 'grassland - :name "grassland" - :ground "tall elephant grass" - :char #\; :col 'yellow - :occupants '((acacia 5) (boulder 1))) ;TODO - -(new-biome 'forest - :name "forest" - :ground "leaf litter and small shrubs" - :char #\. :col 'green - :occupants '((miombo 20) (acacia 10))) ;TODO - -(new-biome 'stream - :name "stream" - :ground "shallow flowing water" - :char #\~ :col 'blue - :occupants '()) ;TODO - -(new-biome 'swamp - :name "swamp" - :ground "short sedge grass growing on boggy black soil" - :char #\w :col 'green - :occupants '()) ;TODO - -(new-biome 'hill - :name "hill" - :ground "hard, stony soil" - :char #\m :col 'white - :occupants '((boulder 15))) ;TODO diff --git a/biome.lisp b/biome.lisp deleted file mode 100644 index fde3c3e..0000000 --- a/biome.lisp +++ /dev/null @@ -1,65 +0,0 @@ -;;;; -;;;; Terra Nostra is a Minecraft-like survival game for the commandline. -;;;; -;;;; Biomes are habitat types, that provide default functions for patches. -;;;; This file defines the biome struct and the inbuilt biomes. -;;;; -;;;; (c) 2018 Daniel Vedder, MIT license -;;;; - -;; Biome struct and register - -(defstruct biome - (name "") - (ground "") - (occupants '()) ;an alist of possible occupants and their probabilities - (char #\.) ;default map display character - (col 'white)) ;default map display colour - - -(let ((biome-list NIL)) - (defun register-biome (symbol-name biome-object) - (setf biome-list (cons (list symbol-name biome-object) biome-list))) - - (defun available-biomes () - (keys biome-list)) - - (defun get-biome (symbol-name) - (cassoc symbol-name biome-list))) - -(defmacro new-biome (name &body body) - `(register-biome ,name - (make-biome ,@body))) - - -;; Biome definitions - -(new-biome 'grassland - :name "grassland" - :ground "tall elephant grass" - :char #\; :col 'yellow - :occupants '((acacia 5) (boulder 1))) ;TODO - -(new-biome 'forest - :name "forest" - :ground "leaf litter and small shrubs" - :char #\. :col 'green - :occupants '((miombo 20) (acacia 10))) ;TODO - -(new-biome 'stream - :name "stream" - :ground "shallow flowing water" - :char #\~ :col 'blue - :occupants '()) ;TODO - -(new-biome 'swamp - :name "swamp" - :ground "short sedge grass growing on boggy black soil" - :char #\w :col 'green - :occupants '()) ;TODO - -(new-biome 'hill - :name "hill" - :ground "hard, stony soil" - :char #\m :col 'white - :occupants '((boulder 15))) ;TODO diff --git a/biomes.lisp b/biomes.lisp new file mode 100644 index 0000000..fde3c3e --- /dev/null +++ b/biomes.lisp @@ -0,0 +1,65 @@ +;;;; +;;;; Terra Nostra is a Minecraft-like survival game for the commandline. +;;;; +;;;; Biomes are habitat types, that provide default functions for patches. +;;;; This file defines the biome struct and the inbuilt biomes. +;;;; +;;;; (c) 2018 Daniel Vedder, MIT license +;;;; + +;; Biome struct and register + +(defstruct biome + (name "") + (ground "") + (occupants '()) ;an alist of possible occupants and their probabilities + (char #\.) ;default map display character + (col 'white)) ;default map display colour + + +(let ((biome-list NIL)) + (defun register-biome (symbol-name biome-object) + (setf biome-list (cons (list symbol-name biome-object) biome-list))) + + (defun available-biomes () + (keys biome-list)) + + (defun get-biome (symbol-name) + (cassoc symbol-name biome-list))) + +(defmacro new-biome (name &body body) + `(register-biome ,name + (make-biome ,@body))) + + +;; Biome definitions + +(new-biome 'grassland + :name "grassland" + :ground "tall elephant grass" + :char #\; :col 'yellow + :occupants '((acacia 5) (boulder 1))) ;TODO + +(new-biome 'forest + :name "forest" + :ground "leaf litter and small shrubs" + :char #\. :col 'green + :occupants '((miombo 20) (acacia 10))) ;TODO + +(new-biome 'stream + :name "stream" + :ground "shallow flowing water" + :char #\~ :col 'blue + :occupants '()) ;TODO + +(new-biome 'swamp + :name "swamp" + :ground "short sedge grass growing on boggy black soil" + :char #\w :col 'green + :occupants '()) ;TODO + +(new-biome 'hill + :name "hill" + :ground "hard, stony soil" + :char #\m :col 'white + :occupants '((boulder 15))) ;TODO diff --git a/biome.lisp b/biome.lisp deleted file mode 100644 index fde3c3e..0000000 --- a/biome.lisp +++ /dev/null @@ -1,65 +0,0 @@ -;;;; -;;;; Terra Nostra is a Minecraft-like survival game for the commandline. -;;;; -;;;; Biomes are habitat types, that provide default functions for patches. -;;;; This file defines the biome struct and the inbuilt biomes. -;;;; -;;;; (c) 2018 Daniel Vedder, MIT license -;;;; - -;; Biome struct and register - -(defstruct biome - (name "") - (ground "") - (occupants '()) ;an alist of possible occupants and their probabilities - (char #\.) ;default map display character - (col 'white)) ;default map display colour - - -(let ((biome-list NIL)) - (defun register-biome (symbol-name biome-object) - (setf biome-list (cons (list symbol-name biome-object) biome-list))) - - (defun available-biomes () - (keys biome-list)) - - (defun get-biome (symbol-name) - (cassoc symbol-name biome-list))) - -(defmacro new-biome (name &body body) - `(register-biome ,name - (make-biome ,@body))) - - -;; Biome definitions - -(new-biome 'grassland - :name "grassland" - :ground "tall elephant grass" - :char #\; :col 'yellow - :occupants '((acacia 5) (boulder 1))) ;TODO - -(new-biome 'forest - :name "forest" - :ground "leaf litter and small shrubs" - :char #\. :col 'green - :occupants '((miombo 20) (acacia 10))) ;TODO - -(new-biome 'stream - :name "stream" - :ground "shallow flowing water" - :char #\~ :col 'blue - :occupants '()) ;TODO - -(new-biome 'swamp - :name "swamp" - :ground "short sedge grass growing on boggy black soil" - :char #\w :col 'green - :occupants '()) ;TODO - -(new-biome 'hill - :name "hill" - :ground "hard, stony soil" - :char #\m :col 'white - :occupants '((boulder 15))) ;TODO diff --git a/biomes.lisp b/biomes.lisp new file mode 100644 index 0000000..fde3c3e --- /dev/null +++ b/biomes.lisp @@ -0,0 +1,65 @@ +;;;; +;;;; Terra Nostra is a Minecraft-like survival game for the commandline. +;;;; +;;;; Biomes are habitat types, that provide default functions for patches. +;;;; This file defines the biome struct and the inbuilt biomes. +;;;; +;;;; (c) 2018 Daniel Vedder, MIT license +;;;; + +;; Biome struct and register + +(defstruct biome + (name "") + (ground "") + (occupants '()) ;an alist of possible occupants and their probabilities + (char #\.) ;default map display character + (col 'white)) ;default map display colour + + +(let ((biome-list NIL)) + (defun register-biome (symbol-name biome-object) + (setf biome-list (cons (list symbol-name biome-object) biome-list))) + + (defun available-biomes () + (keys biome-list)) + + (defun get-biome (symbol-name) + (cassoc symbol-name biome-list))) + +(defmacro new-biome (name &body body) + `(register-biome ,name + (make-biome ,@body))) + + +;; Biome definitions + +(new-biome 'grassland + :name "grassland" + :ground "tall elephant grass" + :char #\; :col 'yellow + :occupants '((acacia 5) (boulder 1))) ;TODO + +(new-biome 'forest + :name "forest" + :ground "leaf litter and small shrubs" + :char #\. :col 'green + :occupants '((miombo 20) (acacia 10))) ;TODO + +(new-biome 'stream + :name "stream" + :ground "shallow flowing water" + :char #\~ :col 'blue + :occupants '()) ;TODO + +(new-biome 'swamp + :name "swamp" + :ground "short sedge grass growing on boggy black soil" + :char #\w :col 'green + :occupants '()) ;TODO + +(new-biome 'hill + :name "hill" + :ground "hard, stony soil" + :char #\m :col 'white + :occupants '((boulder 15))) ;TODO diff --git a/terranostra.lisp b/terranostra.lisp index 93c8e60..a4f8ed9 100644 --- a/terranostra.lisp +++ b/terranostra.lisp @@ -1,158 +1,23 @@ -;#!/usr/bin/clisp ;;;; ;;;; Terra Nostra is a Minecraft-like survival game for the commandline. ;;;; -;;;; This file defines patches and administrates the world object. +;;;; This is the main program file. ;;;; ;;;; (c) 2018 Daniel Vedder, MIT license ;;;; + (defvar *debugging* T) (load "util.lisp") (load "items.lisp") (load "biome.lisp") (load "animals.lisp") +(load "world.lisp") -(defvar *world* NIL) -(defconstant *directions* '(N NE E SE S SW W NW)) - -(defstruct patch - (pos '(0 0)) ;position - (biome NIL) - (items '()) - (occupant NIL)) - - -(defun init-matrix (size) - "Create a square matrix of empty patches" - (debugging "~&Creating a ~S/~S matrix." size size) - (do ((y 0 (1+ y)) (world NIL) (row NIL NIL)) - ((= y size) world) - (dotimes (x size) - (setf row (append row (list (make-patch :pos (list x y)))))) - (setf world (append world (list row))))) - -(defun coord (x y &optional (world *world*)) - "Return the patch at the given coordinates or NIL if out of bounds" - (unless (or (< x 0) (< y 0) (> x (length world)) (> y (length world))) - (nth x (nth y world)))) - -(defun distance (x1 y1 x2 y2) - "Find the distance between two sets of coordinates" - ;;Ignores Pythagoras - (min (abs (- x1 x2)) (abs (- y1 y2)))) - -(defun closest-coords (here coord-list) - "Find the closest position to 'here' from a list of coordinates" - (do* ((clist coord-list (cdr clist)) (c (car clist) (car clist)) - (dist (when c (distance (first here) (second here) - (first c) (second c))) - (when c (distance (first here) (second here) - (first c) (second c)))) - (mindist dist) (closest c)) - ((null clist) closest) - (when (< dist mindist) - (setf mindist dist closest c)))) - -(defun opposite-dir (dir) - "Return the direction opposite the input" - (let ((pos (position dir *directions*))) - (when pos (nth (rem (+ 4 pos) 8) *directions*)))) - -(defun next-dir (dir &optional (cw T)) - "Get the neighbouring direction (clockwise or anticlockwise)" - (let ((pos (position dir *directions*)) - (diff (if cw 1 -1))) - (when pos (nth (rem (+ diff pos 8) 8) *directions*)))) - -(defun dir2patch (herex herey therex therey) - "Calculate the direction to a patch" - (cond ((> herex therex) - (cond ((> herey therey) 'NW) - ((< herey therey) 'SW) - (T 'W))) - ((< herex therex) - (cond ((> herey therey) 'NE) - ((< herey therey) 'SE) - (T 'E))) - (T (cond ((> herey therey) 'N) - ((< herey therey) 'S) - (T NIL))))) - -(defun coordsindir (x y dir) - "Return the coordinates in the given direction" - (cond ((eq dir 'N) (list x (1- y))) - ((eq dir 'NE) (list (1+ x) (1- y))) - ((eq dir 'E) (list (1+ x) y)) - ((eq dir 'SE) (list (1+ x) (1+ y))) - ((eq dir 'S) (list x (1+ y))) - ((eq dir 'SW) (list (1- x) (1+ y))) - ((eq dir 'W) (list (1- x) y)) - ((eq dir 'NW) (list (1- x) (1- y))) - ((null dir) (list x y)) - (T (error "~&Invalid direction ~S")))) - -(defun patchindir (x y dir &optional (world *world*)) - "Return the patch in the given direction" - (let* ((coords (coordsindir x y dir)) - (nextx (first coords)) (nexty (second coords))) - (coord nextx nexty world))) - -(defun neighbour (p dir &optional (world *world*)) - "Return the neighbouring patch in this direction" - (patchindir (first (patch-pos p)) (second (patch-pos p)) dir world)) - -(defun save-topography (file-name &optional (world *world*)) - "Save the world topography as a text file" - (debugging "~&Saving world to file ~A" file-name) ;debug - (with-open-file (tf file-name :direction :output) - (dolist (row world) - (format (if file-name tf T) "~&~A~%" - (string-from-list - (mapcar #'(lambda (p) (biome-char (patch-biome p))) row) - " "))))) - -(defun generate-biomes (size-factor &optional (world *world*)) - ;;XXX The maps this produces don't look as expected, but for - ;; current purposes they are good enough - (debugging "~&Generating biomes") ;debug - (let* ((world-size (length world)) (seeds NIL) - (nseeds (round (/ world-size size-factor))) - (biomes (remove-first-if - #'(lambda (e) (eq e 'stream)) - (available-biomes)))) - ;;Initialize a set of biome 'seed' coordinates - (dotimes (n nseeds) - (setf seeds - (cons (list (random world-size) - (random world-size) - (random-elt biomes)) - seeds))) - (debugging "~&~S" seeds) - ;;For each patch, calculate the closest seed and set to that biome - (dotimes (x world-size) - (dotimes (y world-size) - (let ((b (third (closest-coords (list x y) seeds)))) - (setf (patch-biome (coord x y world)) - (get-biome b))))))) - -(defun generate-stream (x0 y0 &optional (world *world*)) - (debugging "~&Generating a stream, starting at ~S/~S" x0 y0) ;debug - (do* ((dir (random-elt *directions*) - (if (chancep 60) dir (next-dir dir (random-elt '(T NIL))))) - (patch (coord x0 y0 world) (neighbour patch dir world))) - ((or (null patch) (eq (patch-biome patch) (get-biome 'stream)))) - (setf (patch-biome patch) (get-biome 'stream)))) - -(defun create-world (size name &optional (world *world*)) - (setf world NIL) - (setf world (init-matrix size)) - ;;XXX magic numbers - (generate-biomes 20 world) - (dotimes (s (round (/ (expt size 2) 500))) - (generate-stream (random size) (random size) world)) - (save-topography name world)) ;; Initialize the random state (which would otherwise not be very random...) (setf *random-state* (make-random-state t)) + +(create-world 100 "test.world") + diff --git a/biome.lisp b/biome.lisp deleted file mode 100644 index fde3c3e..0000000 --- a/biome.lisp +++ /dev/null @@ -1,65 +0,0 @@ -;;;; -;;;; Terra Nostra is a Minecraft-like survival game for the commandline. -;;;; -;;;; Biomes are habitat types, that provide default functions for patches. -;;;; This file defines the biome struct and the inbuilt biomes. -;;;; -;;;; (c) 2018 Daniel Vedder, MIT license -;;;; - -;; Biome struct and register - -(defstruct biome - (name "") - (ground "") - (occupants '()) ;an alist of possible occupants and their probabilities - (char #\.) ;default map display character - (col 'white)) ;default map display colour - - -(let ((biome-list NIL)) - (defun register-biome (symbol-name biome-object) - (setf biome-list (cons (list symbol-name biome-object) biome-list))) - - (defun available-biomes () - (keys biome-list)) - - (defun get-biome (symbol-name) - (cassoc symbol-name biome-list))) - -(defmacro new-biome (name &body body) - `(register-biome ,name - (make-biome ,@body))) - - -;; Biome definitions - -(new-biome 'grassland - :name "grassland" - :ground "tall elephant grass" - :char #\; :col 'yellow - :occupants '((acacia 5) (boulder 1))) ;TODO - -(new-biome 'forest - :name "forest" - :ground "leaf litter and small shrubs" - :char #\. :col 'green - :occupants '((miombo 20) (acacia 10))) ;TODO - -(new-biome 'stream - :name "stream" - :ground "shallow flowing water" - :char #\~ :col 'blue - :occupants '()) ;TODO - -(new-biome 'swamp - :name "swamp" - :ground "short sedge grass growing on boggy black soil" - :char #\w :col 'green - :occupants '()) ;TODO - -(new-biome 'hill - :name "hill" - :ground "hard, stony soil" - :char #\m :col 'white - :occupants '((boulder 15))) ;TODO diff --git a/biomes.lisp b/biomes.lisp new file mode 100644 index 0000000..fde3c3e --- /dev/null +++ b/biomes.lisp @@ -0,0 +1,65 @@ +;;;; +;;;; Terra Nostra is a Minecraft-like survival game for the commandline. +;;;; +;;;; Biomes are habitat types, that provide default functions for patches. +;;;; This file defines the biome struct and the inbuilt biomes. +;;;; +;;;; (c) 2018 Daniel Vedder, MIT license +;;;; + +;; Biome struct and register + +(defstruct biome + (name "") + (ground "") + (occupants '()) ;an alist of possible occupants and their probabilities + (char #\.) ;default map display character + (col 'white)) ;default map display colour + + +(let ((biome-list NIL)) + (defun register-biome (symbol-name biome-object) + (setf biome-list (cons (list symbol-name biome-object) biome-list))) + + (defun available-biomes () + (keys biome-list)) + + (defun get-biome (symbol-name) + (cassoc symbol-name biome-list))) + +(defmacro new-biome (name &body body) + `(register-biome ,name + (make-biome ,@body))) + + +;; Biome definitions + +(new-biome 'grassland + :name "grassland" + :ground "tall elephant grass" + :char #\; :col 'yellow + :occupants '((acacia 5) (boulder 1))) ;TODO + +(new-biome 'forest + :name "forest" + :ground "leaf litter and small shrubs" + :char #\. :col 'green + :occupants '((miombo 20) (acacia 10))) ;TODO + +(new-biome 'stream + :name "stream" + :ground "shallow flowing water" + :char #\~ :col 'blue + :occupants '()) ;TODO + +(new-biome 'swamp + :name "swamp" + :ground "short sedge grass growing on boggy black soil" + :char #\w :col 'green + :occupants '()) ;TODO + +(new-biome 'hill + :name "hill" + :ground "hard, stony soil" + :char #\m :col 'white + :occupants '((boulder 15))) ;TODO diff --git a/terranostra.lisp b/terranostra.lisp index 93c8e60..a4f8ed9 100644 --- a/terranostra.lisp +++ b/terranostra.lisp @@ -1,158 +1,23 @@ -;#!/usr/bin/clisp ;;;; ;;;; Terra Nostra is a Minecraft-like survival game for the commandline. ;;;; -;;;; This file defines patches and administrates the world object. +;;;; This is the main program file. ;;;; ;;;; (c) 2018 Daniel Vedder, MIT license ;;;; + (defvar *debugging* T) (load "util.lisp") (load "items.lisp") (load "biome.lisp") (load "animals.lisp") +(load "world.lisp") -(defvar *world* NIL) -(defconstant *directions* '(N NE E SE S SW W NW)) - -(defstruct patch - (pos '(0 0)) ;position - (biome NIL) - (items '()) - (occupant NIL)) - - -(defun init-matrix (size) - "Create a square matrix of empty patches" - (debugging "~&Creating a ~S/~S matrix." size size) - (do ((y 0 (1+ y)) (world NIL) (row NIL NIL)) - ((= y size) world) - (dotimes (x size) - (setf row (append row (list (make-patch :pos (list x y)))))) - (setf world (append world (list row))))) - -(defun coord (x y &optional (world *world*)) - "Return the patch at the given coordinates or NIL if out of bounds" - (unless (or (< x 0) (< y 0) (> x (length world)) (> y (length world))) - (nth x (nth y world)))) - -(defun distance (x1 y1 x2 y2) - "Find the distance between two sets of coordinates" - ;;Ignores Pythagoras - (min (abs (- x1 x2)) (abs (- y1 y2)))) - -(defun closest-coords (here coord-list) - "Find the closest position to 'here' from a list of coordinates" - (do* ((clist coord-list (cdr clist)) (c (car clist) (car clist)) - (dist (when c (distance (first here) (second here) - (first c) (second c))) - (when c (distance (first here) (second here) - (first c) (second c)))) - (mindist dist) (closest c)) - ((null clist) closest) - (when (< dist mindist) - (setf mindist dist closest c)))) - -(defun opposite-dir (dir) - "Return the direction opposite the input" - (let ((pos (position dir *directions*))) - (when pos (nth (rem (+ 4 pos) 8) *directions*)))) - -(defun next-dir (dir &optional (cw T)) - "Get the neighbouring direction (clockwise or anticlockwise)" - (let ((pos (position dir *directions*)) - (diff (if cw 1 -1))) - (when pos (nth (rem (+ diff pos 8) 8) *directions*)))) - -(defun dir2patch (herex herey therex therey) - "Calculate the direction to a patch" - (cond ((> herex therex) - (cond ((> herey therey) 'NW) - ((< herey therey) 'SW) - (T 'W))) - ((< herex therex) - (cond ((> herey therey) 'NE) - ((< herey therey) 'SE) - (T 'E))) - (T (cond ((> herey therey) 'N) - ((< herey therey) 'S) - (T NIL))))) - -(defun coordsindir (x y dir) - "Return the coordinates in the given direction" - (cond ((eq dir 'N) (list x (1- y))) - ((eq dir 'NE) (list (1+ x) (1- y))) - ((eq dir 'E) (list (1+ x) y)) - ((eq dir 'SE) (list (1+ x) (1+ y))) - ((eq dir 'S) (list x (1+ y))) - ((eq dir 'SW) (list (1- x) (1+ y))) - ((eq dir 'W) (list (1- x) y)) - ((eq dir 'NW) (list (1- x) (1- y))) - ((null dir) (list x y)) - (T (error "~&Invalid direction ~S")))) - -(defun patchindir (x y dir &optional (world *world*)) - "Return the patch in the given direction" - (let* ((coords (coordsindir x y dir)) - (nextx (first coords)) (nexty (second coords))) - (coord nextx nexty world))) - -(defun neighbour (p dir &optional (world *world*)) - "Return the neighbouring patch in this direction" - (patchindir (first (patch-pos p)) (second (patch-pos p)) dir world)) - -(defun save-topography (file-name &optional (world *world*)) - "Save the world topography as a text file" - (debugging "~&Saving world to file ~A" file-name) ;debug - (with-open-file (tf file-name :direction :output) - (dolist (row world) - (format (if file-name tf T) "~&~A~%" - (string-from-list - (mapcar #'(lambda (p) (biome-char (patch-biome p))) row) - " "))))) - -(defun generate-biomes (size-factor &optional (world *world*)) - ;;XXX The maps this produces don't look as expected, but for - ;; current purposes they are good enough - (debugging "~&Generating biomes") ;debug - (let* ((world-size (length world)) (seeds NIL) - (nseeds (round (/ world-size size-factor))) - (biomes (remove-first-if - #'(lambda (e) (eq e 'stream)) - (available-biomes)))) - ;;Initialize a set of biome 'seed' coordinates - (dotimes (n nseeds) - (setf seeds - (cons (list (random world-size) - (random world-size) - (random-elt biomes)) - seeds))) - (debugging "~&~S" seeds) - ;;For each patch, calculate the closest seed and set to that biome - (dotimes (x world-size) - (dotimes (y world-size) - (let ((b (third (closest-coords (list x y) seeds)))) - (setf (patch-biome (coord x y world)) - (get-biome b))))))) - -(defun generate-stream (x0 y0 &optional (world *world*)) - (debugging "~&Generating a stream, starting at ~S/~S" x0 y0) ;debug - (do* ((dir (random-elt *directions*) - (if (chancep 60) dir (next-dir dir (random-elt '(T NIL))))) - (patch (coord x0 y0 world) (neighbour patch dir world))) - ((or (null patch) (eq (patch-biome patch) (get-biome 'stream)))) - (setf (patch-biome patch) (get-biome 'stream)))) - -(defun create-world (size name &optional (world *world*)) - (setf world NIL) - (setf world (init-matrix size)) - ;;XXX magic numbers - (generate-biomes 20 world) - (dotimes (s (round (/ (expt size 2) 500))) - (generate-stream (random size) (random size) world)) - (save-topography name world)) ;; Initialize the random state (which would otherwise not be very random...) (setf *random-state* (make-random-state t)) + +(create-world 100 "test.world") + diff --git a/world.lisp b/world.lisp new file mode 100644 index 0000000..64275ee --- /dev/null +++ b/world.lisp @@ -0,0 +1,152 @@ +;;;; +;;;; Terra Nostra is a Minecraft-like survival game for the commandline. +;;;; +;;;; This file defines patches and administrates the world object. +;;;; +;;;; (c) 2018 Daniel Vedder, MIT license +;;;; + +(defvar *world* NIL) +(defconstant *directions* '(N NE E SE S SW W NW)) + +(defstruct patch + (pos '(0 0)) ;position + (biome NIL) + (items '()) + (occupant NIL)) + +;; MATRIX FUNCTIONS + +(defun init-matrix (size) + "Create a square matrix of empty patches" + ;;TODO change this to arrays for performance + (debugging "~&Creating a ~S/~S matrix." size size) + (do ((y 0 (1+ y)) (world NIL) (row NIL NIL)) + ((= y size) world) + (dotimes (x size) + (setf row (append row (list (make-patch :pos (list x y)))))) + (setf world (append world (list row))))) + +(defun coord (x y &optional (world *world*)) + "Return the patch at the given coordinates or NIL if out of bounds" + (unless (or (< x 0) (< y 0) (> x (length world)) (> y (length world))) + (nth x (nth y world)))) + +(defun distance (x1 y1 x2 y2) + "Find the distance between two sets of coordinates" + ;;Ignores Pythagoras + (min (abs (- x1 x2)) (abs (- y1 y2)))) + +(defun closest-coords (here coord-list) + "Find the closest position to 'here' from a list of coordinates" + (do* ((clist coord-list (cdr clist)) (c (car clist) (car clist)) + (dist (when c (distance (first here) (second here) + (first c) (second c))) + (when c (distance (first here) (second here) + (first c) (second c)))) + (mindist dist) (closest c)) + ((null clist) closest) + (when (< dist mindist) + (setf mindist dist closest c)))) + +(defun opposite-dir (dir) + "Return the direction opposite the input" + (let ((pos (position dir *directions*))) + (when pos (nth (rem (+ 4 pos) 8) *directions*)))) + +(defun next-dir (dir &optional (cw T)) + "Get the neighbouring direction (clockwise or anticlockwise)" + (let ((pos (position dir *directions*)) + (diff (if cw 1 -1))) + (when pos (nth (rem (+ diff pos 8) 8) *directions*)))) + +(defun dir2patch (herex herey therex therey) + "Calculate the direction to a patch" + (cond ((> herex therex) + (cond ((> herey therey) 'NW) + ((< herey therey) 'SW) + (T 'W))) + ((< herex therex) + (cond ((> herey therey) 'NE) + ((< herey therey) 'SE) + (T 'E))) + (T (cond ((> herey therey) 'N) + ((< herey therey) 'S) + (T NIL))))) + +(defun coordsindir (x y dir) + "Return the coordinates in the given direction" + (cond ((eq dir 'N) (list x (1- y))) + ((eq dir 'NE) (list (1+ x) (1- y))) + ((eq dir 'E) (list (1+ x) y)) + ((eq dir 'SE) (list (1+ x) (1+ y))) + ((eq dir 'S) (list x (1+ y))) + ((eq dir 'SW) (list (1- x) (1+ y))) + ((eq dir 'W) (list (1- x) y)) + ((eq dir 'NW) (list (1- x) (1- y))) + ((null dir) (list x y)) + (T (error "~&Invalid direction ~S")))) + +(defun patchindir (x y dir &optional (world *world*)) + "Return the patch in the given direction" + (let* ((coords (coordsindir x y dir)) + (nextx (first coords)) (nexty (second coords))) + (coord nextx nexty world))) + +(defun neighbour (p dir &optional (world *world*)) + "Return the neighbouring patch in this direction" + (patchindir (first (patch-pos p)) (second (patch-pos p)) dir world)) + + +;; TOPOGRAPHY FUNCTIONS + +(defun save-topography (file-name &optional (world *world*)) + "Save the world topography as a text file" + (debugging "~&Saving world to file ~A" file-name) ;debug + (with-open-file (tf file-name :direction :output) + (dolist (row world) + (format (if file-name tf T) "~&~A~%" + (string-from-list + (mapcar #'(lambda (p) (biome-char (patch-biome p))) row) + " "))))) + +(defun generate-biomes (size-factor &optional (world *world*)) + ;;XXX The maps this produces don't look as expected, but for + ;; current purposes they are good enough + (debugging "~&Generating biomes") ;debug + (let* ((world-size (length world)) (seeds NIL) + (nseeds (round (/ world-size size-factor))) + (biomes (remove-first-if + #'(lambda (e) (eq e 'stream)) + (available-biomes)))) + ;;Initialize a set of biome 'seed' coordinates + (dotimes (n nseeds) + (setf seeds + (cons (list (random world-size) + (random world-size) + (random-elt biomes)) + seeds))) + (debugging "~&~S" seeds) + ;;For each patch, calculate the closest seed and set to that biome + (dotimes (x world-size) + (dotimes (y world-size) + (let ((b (third (closest-coords (list x y) seeds)))) + (setf (patch-biome (coord x y world)) + (get-biome b))))))) + +(defun generate-stream (x0 y0 &optional (world *world*)) + (debugging "~&Generating a stream, starting at ~S/~S" x0 y0) ;debug + (do* ((dir (random-elt *directions*) + (if (chancep 60) dir (next-dir dir (random-elt '(T NIL))))) + (patch (coord x0 y0 world) (neighbour patch dir world))) + ((or (null patch) (eq (patch-biome patch) (get-biome 'stream)))) + (setf (patch-biome patch) (get-biome 'stream)))) + +(defun create-world (size &optional name (world *world*)) + (setf world NIL) + (setf world (init-matrix size)) + ;;XXX magic numbers + (generate-biomes 20 world) + (dotimes (s (round (/ (expt size 2) 500))) + (generate-stream (random size) (random size) world)) + (when name (save-topography name world)))