src/main.lisp @ fa45164eab85

Add random food
author Steve Losh <steve@stevelosh.com>
date Sat, 07 Jan 2017 17:48:08 +0000
parents f17271ef79b7
children 9dbe31fef037
(in-package :ap)


;;;; Data ---------------------------------------------------------------------
(defparameter *logo* (read-file-into-string "data/logo.txt"))
(defparameter *intro1* (read-file-into-string "data/intro1.txt"))
(defparameter *intro2* (read-file-into-string "data/intro2.txt"))
(defparameter *intro3* (read-file-into-string "data/intro3.txt"))
(defparameter *intro4* (read-file-into-string "data/intro4.txt"))
(defparameter *intro5* (read-file-into-string "data/intro5.txt"))
(defparameter *intro6* (read-file-into-string "data/intro6.txt"))

(defparameter *screen-width* nil)
(defparameter *screen-height* nil)

(defparameter *width* nil)
(defparameter *height* nil)

(defparameter *terrain* nil)

(defparameter *view-x* nil)
(defparameter *view-y* nil)

(defparameter *wat* nil)
(defparameter *player* nil)
(defparameter *sidebar-width* 30)
(defparameter *food-density* 1/6000)


;;;; Colors -------------------------------------------------------------------
(defcolors
  (+white-black+  charms/ll:COLOR_WHITE   charms/ll:COLOR_BLACK)
  (+blue-black+   charms/ll:COLOR_BLUE    charms/ll:COLOR_BLACK)
  (+cyan-black+   charms/ll:COLOR_CYAN    charms/ll:COLOR_BLACK)
  (+yellow-black+ charms/ll:COLOR_YELLOW  charms/ll:COLOR_BLACK)
  (+green-black+  charms/ll:COLOR_GREEN   charms/ll:COLOR_BLACK)
  (+pink-black+   charms/ll:COLOR_MAGENTA charms/ll:COLOR_BLACK)

  (+black-white+  charms/ll:COLOR_BLACK   charms/ll:COLOR_WHITE)
  )


;;;; Intro --------------------------------------------------------------------
(defmacro dialog (&body body)
  `(with-dims (50 10)
     (with-panel-and-window
         (pan win *width* *height*
              (center *width* *screen-width*)
              (center *height* *screen-height*))
       (charms:clear-window win)
       (border win)
       ,@body
       (write-string-right win "Press any key" -1 (1- *height*))
       (redraw)
       (charms:get-char win))))

(defun intro1 ()
  (if (eq :left (dialog (write-lines-left win *intro1* 1 1)))
    (title)
    (intro2)))

(defun intro2 ()
  (if (eq :left (dialog (write-lines-left win *intro2* 1 1)))
    (intro1)
    (intro3)))

(defun intro3 ()
  (if (eq :left (dialog (write-lines-left win *intro3* 1 1)))
    (intro2)
    (intro4)))

(defun intro4 ()
  (if (eq :left (dialog (write-lines-left win *intro4* 1 1)))
    (intro3)
    (intro5)))

(defun intro5 ()
  (if (eq :left (dialog (write-lines-left win *intro5* 1 1)))
    (intro4)
    (intro6)))

(defun intro6 ()
  (if (eq :left (dialog (write-lines-left win *intro6* 1 1)))
    (intro5)
    (generate-world)))


;;;; Title --------------------------------------------------------------------
(defun title ()
  (with-dims (50 10)
    (with-panel-and-window
        (pan win *width* *height*
             (center *width* *screen-width*)
             (center *height* *screen-height*))
      (write-lines-centered win *logo* 0)
      (redraw)
      (charms:get-char win)))
  ; (intro1)
  (generate-world)
  )


;;;; World Generation ---------------------------------------------------------
(defun underwaterp (height)
  (< height 0.05))

(defun generate-terrain ()
  (setf *terrain* (ap.generation::generate-heightmap)
        *view-x* 0 *view-y* 0))

(defun spawn-player ()
  (setf *player* (make-player)))

(defun place-food ()
  (iterate
    (with remaining = (round (* *food-density*
                                ap.generation::*map-size*
                                ap.generation::*map-size*)))
    (until (zerop remaining))
    (for x = (random ap.generation::*map-size*))
    (for y = (random ap.generation::*map-size*))
    (when (not (underwaterp (aref *terrain* x y)))
      (make-food x y)
      (decf remaining))))

(defun generate-world ()
  (clear-entities)
  (with-dims (30 (+ 2 3))
    (with-panel-and-window
        (pan win *width* *height*
             (center *width* *screen-width*)
             (center *height* *screen-height*))
      (border win)
      (progn (write-string-left win "Generating terrain..." 1 1)
             (redraw)
             (generate-terrain))
      (progn (write-string-left win "Placing food..." 1 2)
             (redraw)
             (place-food))
      (progn (write-string-left win "Spawning player..." 1 3)
             (redraw)
             (spawn-player))))
  (world-map))


;;;; World Map ----------------------------------------------------------------
(defun terrain-char (height)
  (cond ((< height -0.20) (values #\~ +blue-black+)) ; deep water
        ((< height -0.05) (values #\~ +cyan-black+)) ; shallow water
        ((< height  0.05) (values #\` +yellow-black+)) ; sand
        ((< height  0.40) (values #\. +white-black+)) ; dirt
        ((< height  0.55) (values #\^ +white-black+)) ; hills
        (t                (values #\# +white-black+)))) ; mountains

(defun clamp-view (coord size)
  (clamp 0 (- ap.generation::*map-size* size 1) coord))

(defun center-view (width height x y)
  (setf *view-x* (clamp-view (- x (truncate width 2)) width)
        *view-y* (clamp-view (- y (truncate height 2)) height)))

(defun center-view-on-player (width height)
  (center-view width height
               (coords/x *player*)
               (coords/y *player*)))

(defun render-items (window)
  (let ((items (-<> (coords-lookup (coords/x *player*)
                                   (coords/y *player*))
                 (remove-if-not #'holdable? <>))))
    (when items
      (if (= (length items) 1)
        (write-string-left window "The following thing is here:" 0 0)
        (write-string-left window "The following things are here:" 0 0))
      (iterate
        (for item :in items)
        (for y :from 1)
        (write-string-left window
                           (format nil "  ~A" (holdable/description item))
                           0 1)))))

(defun render-map (window)
  (iterate
    (with terrain = *terrain*)
    (with vx = *view-x*)
    (with vy = *view-y*)
    (for-nested ((sx :from 0 :below *width*)
                 (sy :from 0 :below *height*)))
    (for x = (+ sx vx))
    (for y = (+ sy vy))
    (for (values glyph color) = (terrain-char (aref terrain x y)))
    (with-color (window color)
      (charms:write-char-at-point window glyph sx sy))
    (for entities = (coords-lookup x y))
    (for entity = (if (member *player* entities)
                    *player*
                    (find-if #'visible? entities)))
    (when entity
      (with-color (window (visible/color entity))
        (charms:write-string-at-point window (visible/glyph entity) sx sy)))))


(defun render-sidebar (window)
  (charms:clear-window window)
  (border window)
  (let ((p *player*))
    (write-string-left window (format nil "You are ~A" (health-description
                                                         (player/health p)))
                       1 1)
    (write-string-left window (format nil "        ~A" (energy-description
                                                         (player/energy p)))
                       1 2)
    (write-string-left window (format nil "You are carrying:") 1 4)
    (if (player-inventory-empty-p p)
      (write-string-left window (format nil "Nothing") 3 5)
      (iterate
        (for item :in (player/inventory p))
        (for y :from 5)
        (write-lines-left window
                          (cl-strings:shorten (holdable/description item)
                                              (- *width* 2 2 3 1))
                          3 y)))))


(defun move-player (dx dy)
  (let ((player *player*))
    (coords-move-entity player
                        (+ (coords/x player) dx)
                        (+ (coords/y player) dy))))

(defun world-map-input (window)
  (case (charms:get-char window)
    (#\q :quit)
    (:left  (move-player -1 0) :tick)
    (:right (move-player 1 0) :tick)
    (:up    (move-player 0 -1) :tick)
    (:down  (move-player 0 1) :tick)))


(defun world-map ()
  (with-dims ((- *screen-width* 2) (- *screen-height* 1))
    (with-panels-and-windows
        ((map-pan map-win (- *width* *sidebar-width*) *height* 0 0)
         (bar-pan bar-win *sidebar-width* *height* (- *width* *sidebar-width*) 0))
      (iterate
        (with-window-dims bar-win
          (render-sidebar bar-win))
        (with-window-dims map-win
          (center-view-on-player *width* *height*)
          (render-map map-win)
          (render-items map-win))
        (redraw)
        (case (world-map-input bar-win)
          (:tick (tick-player *player*))
          (:quit (return))))))
  nil)


;;;; Main ---------------------------------------------------------------------
(defun main ()
  (charms:with-curses ()
    (charms:disable-echoing)
    (charms:enable-raw-input :interpret-control-characters t)
    (charms:enable-extra-keys t)
    (charms/ll:start-color)
    (charms:clear-window t)
    (init-colors)

    ; todo: handle resizes
    (setf (values *screen-width* *screen-height*)
          (charms:window-dimensions t))

    (let ((*width* *screen-width*)
          (*height* *screen-height*))
      (title)))

  t)