diff --git a/animals.lisp b/animals.lisp index da62e2e..a0428f2 100644 --- a/animals.lisp +++ b/animals.lisp @@ -39,7 +39,7 @@ (cassoc symbol-name species-list))) (defmacro new-species (name &body body) - `(register-species name + `(register-species ,name (make-species ,@body))) (let ((max-id 0)) diff --git a/animals.lisp b/animals.lisp index da62e2e..a0428f2 100644 --- a/animals.lisp +++ b/animals.lisp @@ -39,7 +39,7 @@ (cassoc symbol-name species-list))) (defmacro new-species (name &body body) - `(register-species name + `(register-species ,name (make-species ,@body))) (let ((max-id 0)) diff --git a/terranostra.lisp b/terranostra.lisp index 917698b..93c8e60 100644 --- a/terranostra.lisp +++ b/terranostra.lisp @@ -7,6 +7,8 @@ ;;;; (c) 2018 Daniel Vedder, MIT license ;;;; +(defvar *debugging* T) + (load "util.lisp") (load "items.lisp") (load "biome.lisp") @@ -17,13 +19,14 @@ (defstruct patch (pos '(0 0)) ;position - (biome (get-biome 'grassland)) + (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) @@ -35,6 +38,23 @@ (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*))) @@ -84,42 +104,54 @@ (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 csv file" + "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) - ;; TODO This would be quicker with (string-from-list) - (do ((x 0 (1+ x)) (xstr "")) - ((= x (length world)) (format tf "~&~A~%" xstr)) - (setf xstr (concatenate 'string xstr (unless (= x 0) ",") - (format NIL "~S" - (biome-char (patch-biome (nth x row)))))))))) - - -(defun generate-biome-patch (biome-type x y width &optional (world *world*)) - ;;TODO - ) - + (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) (neighbour patch dir))) + (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*) - (biomes '(forest hill)) (size-factor 30)) +(defun create-world (size name &optional (world *world*)) (setf world NIL) (setf world (init-matrix size)) ;;XXX magic numbers - (dotimes (s (round (/ (expt size 2) (* size-factor 10)))) + (generate-biomes 20 world) + (dotimes (s (round (/ (expt size 2) 500))) (generate-stream (random size) (random size) world)) - (dotimes (f (round (/ (expt size 2) (expt size-factor 2)))) - (generate-biome-patch 'forest (random size) (random size) - (random size-factor) world)) - (dotimes (h (round (/ (expt size 2) (* 1.5 (expt size-factor 2))))) - (generate-biome-patch 'hill (random size) (random size) - (random (round (/ 2 size-factor))) world)) - ;;TODO (save-topography name world)) ;; Initialize the random state (which would otherwise not be very random...)