75152f6efda6

Pivot
[view raw] [browse files]
author Steve Losh <steve@stevelosh.com>
date Tue, 20 Apr 2021 18:01:32 -0400
parents 614ad4a1d44e
children 60483297353d
branches/tags (none)
files assets/help.txt assets/journal.txt assets/splash.txt dark.asd mycelium.asd scratch.lisp scripts/build.sh scripts/deploy.sh src/main.lisp src/package.lisp

Changes

--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/assets/help.txt	Tue Apr 20 18:01:32 2021 -0400
@@ -0,0 +1,2 @@
+[esc] Pause (with options to resume/quit)
+[tab] Switch between panels.
--- a/assets/journal.txt	Tue Apr 20 15:13:04 2021 -0400
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,4 +0,0 @@
-September 22, 1987
-Finally got access to a computer terminal.
-
-Cosmic Hyperspace Interferometer R Operations Network
--- a/assets/splash.txt	Tue Apr 20 15:13:04 2021 -0400
+++ b/assets/splash.txt	Tue Apr 20 18:01:32 2021 -0400
@@ -1,5 +1,11 @@
-TODO: title
+███╗   ███╗██╗   ██╗ ██████╗███████╗██╗     ██╗██╗   ██╗███╗   ███╗
+████╗ ████║╚██╗ ██╔╝██╔════╝██╔════╝██║     ██║██║   ██║████╗ ████║
+██╔████╔██║ ╚████╔╝ ██║     █████╗  ██║     ██║██║   ██║██╔████╔██║
+██║╚██╔╝██║  ╚██╔╝  ██║     ██╔══╝  ██║     ██║██║   ██║██║╚██╔╝██║
+██║ ╚═╝ ██║   ██║   ╚██████╗███████╗███████╗██║╚██████╔╝██║ ╚═╝ ██║
+╚═╝     ╚═╝   ╚═╝    ╚═════╝╚══════╝╚══════╝╚═╝ ╚═════╝ ╚═╝     ╚═╝
 by Steve Losh
 for the Lisp Game Jam, Spring 2021
 
-Press any key to start.
+                                      Press ? at any time for help.
+                                              Press space to start.
--- a/dark.asd	Tue Apr 20 15:13:04 2021 -0400
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,22 +0,0 @@
-(asdf:defsystem :dark
-  :name "dark"
-  :description "Lisp Game Jam, Spring 2021"
-
-  :author "Steve Losh <steve@stevelosh.com>"
-
-  :license "MIT/X11"
-  :version "0.0.1"
-
-  :depends-on (#+sbcl #:sb-sprof
-               :alexandria
-               :boots
-               :cl-pcg
-               :iterate
-               :local-time
-               :losh)
-
-  :serial t
-  :components
-  ((:module "src" :serial t
-    :components ((:file "package")
-                 (:file "main")))))
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/mycelium.asd	Tue Apr 20 18:01:32 2021 -0400
@@ -0,0 +1,24 @@
+(asdf:defsystem :mycelium
+  :name "mycelium"
+  :description "Lisp Game Jam, Spring 2021"
+
+  :author "Steve Losh <steve@stevelosh.com>"
+
+  :license "MIT/X11"
+  :version "0.0.1"
+
+  :depends-on (#+sbcl #:sb-sprof
+               :alexandria
+               :boots
+               :str
+               :chancery
+               :cl-pcg
+               :iterate
+               :local-time
+               :losh)
+
+  :serial t
+  :components
+  ((:module "src" :serial t
+    :components ((:file "package")
+                 (:file "main")))))
--- a/scratch.lisp	Tue Apr 20 15:13:04 2021 -0400
+++ b/scratch.lisp	Tue Apr 20 18:01:32 2021 -0400
@@ -1,4 +1,4 @@
-(in-package :dark)
+(in-package :mycelium)
 
 (ql:quickload :bordeaux-threads)
 
@@ -12,18 +12,29 @@
 (boots%::print-attr
   (boots:attr :fg (boots:rgb 1 2 3)))
 
-
-
-
-
-
-
+(defun key-name (key)
+  (case key 
+    (#\tab "tab")
+    (t (string key))))
 
-abc
-len = 3
-wid = 10
-10 - 3 = 7
+(defun select-box (heading options)
+  (with-chiron-layer
+      (boots:stack (:margin t :width 20 :height (+ (length options) 2) :border t
+                    :fill-char #\space :fill-attr +default+)
+        (boots:canvas (:fill-char #\space :fill-attr +default+ :height 1 :border-bottom t) (pad)
+          (boots:draw pad
+                      (truncate (- (boots:width pad) (length heading)) 2)
+                      0 heading +default+))
+        (boots:canvas (:fill-char #\space :fill-attr +default+) (pad)
+          (iterate (for y :from 0)
+                   (for (key description result) :in options)
+                   (boots:draw pad 0 y (format nil "[~A] ~A" (key-name key)
+                                               description) +default+))))
+    (third (iterate
+             (boots:redraw)
+             (for (values e m) = (boots:read-event))
+             (unless (handle-global-event e m)
+               (thereis (find-if (lambda (option)
+                                   (boots:event= option e m))
+                                 options :key #'first)))))))
 
-0123456789
-
-
--- a/scripts/build.sh	Tue Apr 20 15:13:04 2021 -0400
+++ b/scripts/build.sh	Tue Apr 20 18:01:32 2021 -0400
@@ -4,7 +4,7 @@
 
 ban Building
 /usr/local/bin/sbcl \
-    --eval '(ql:quickload :dark)' \
-    --eval '(dark:build)' \
+    --eval '(ql:quickload :mycelium)' \
+    --eval '(mycelium:build)' \
     --disable-debugger --noinform
 
--- a/scripts/deploy.sh	Tue Apr 20 15:13:04 2021 -0400
+++ b/scripts/deploy.sh	Tue Apr 20 18:01:32 2021 -0400
@@ -5,4 +5,4 @@
 ./scripts/build.sh
 
 ban Deploying
-time rsync -avz build/dark jam:/opt/dark/dark
+time rsync -avz build/mycelium jam:/opt/mycelium/mycelium
--- a/src/main.lisp	Tue Apr 20 15:13:04 2021 -0400
+++ b/src/main.lisp	Tue Apr 20 18:01:32 2021 -0400
@@ -1,43 +1,51 @@
-(in-package :dark)
+(in-package :mycelium)
 
-;;;; Global -------------------------------------------------------------------
+;;;; Constants ----------------------------------------------------------------
 (defconstant +black+ (boots:rgb 0 0 0))
 (defconstant +white+ (boots:rgb 255 255 255))
-(defconstant +amber+ (boots:rgb #xFB #x7D #x01))
-(defconstant +default+ (boots:attr :fg +amber+ :bg +black+))
-(defconstant +reverse+ (boots:attr :fg +black+ :bg +amber+))
-(defconstant +ooc+ (boots:attr :fg +white+ :bg +black+))
+(defconstant +dark-purple+ (boots:rgb #x52 #x2F #x70))
 
+(defconstant +default+ (boots:attr :fg +white+ :bg +black+))
+(defconstant +bold+ (boots:attr :fg +white+ :bg +black+ :bold t))
+(defconstant +reverse+ (boots:attr :fg +black+ :bg +white+))
+(defconstant +selected+ (boots:attr :fg +white+ :bg +dark-purple+ :bold t))
+
+
+;;;; State --------------------------------------------------------------------
 (defvar *event* nil)
 (defvar *mods* nil)
+(defvar *seed* nil)
+(defvar *debug* (make-ring-buffer))
+(defvar *pcg* (pcg:make-pcg))
+(defvar *elapsed* nil)
+(defvar *panels* nil)
+(defvar *unlocked* nil)
+(defvar *messages* (make-ring-buffer :size 16))
+(defvar *calories* 0.0)
 
-(defvar *countdown* nil)
-(defvar *seed* nil)
-
+;;;; Config -------------------------------------------------------------------
+(defparameter *forage-chance* 0.1)
+(defparameter *calories-per-second* 1.0)
 
 ;;;; Assets -------------------------------------------------------------------
-(defparameter *asset/splash* (alexandria:read-file-into-string "assets/splash.txt"))
-(defparameter *asset/journal* (alexandria:read-file-into-string "assets/journal.txt"))
+(defun load-asset (path)
+  (_ path
+    alexandria:read-file-into-string
+    (string-right-trim '(#\newline) _)
+    str:lines))
+
+(defparameter *asset/splash* (load-asset "assets/splash.txt"))
+(defparameter *asset/help* (load-asset "assets/help.txt"))
 
 
 ;;;; Utils --------------------------------------------------------------------
-(defparameter *pcg* (pcg:make-pcg))
-
 (defun random (bound &optional max inclusive?)
   (pcg:pcg-random *pcg* bound max inclusive?))
 
-(defun press-any-key ()
-  (boots:read-event)
+(defun press-key (event)
+  (loop :until (multiple-value-call #'boots:event= event (boots:read-event)))
   (values))
 
-(defmacro with-in-game-colors (&body body)
-  `(let ((boots:*border-attr* +default+))
-     ,@body))
-
-(defmacro with-ooc-colors (&body body)
-  `(let ((boots:*border-attr* +ooc+))
-     ,@body))
-
 (defmacro with-ui (ui &body body)
   (alexandria:with-gensyms (prev)
     `(let ((,prev (boots:root boots:*screen*)))
@@ -56,13 +64,18 @@
   (boots:event-case (values event mods)
     (#(:ctrl #\q) (throw 'quit nil))
     (#\esc (pause) t)
+    (#\? (help) t)
     (t nil)))
 
 (defun draw-right (pad x y string attr)
   (boots:draw pad (1+ (- x (length string))) y string attr))
 
-
-(defvar *debug* (make-ring-buffer))
+(defun draw-center (pad y string attr &aux
+                    (s (string string))
+                    (w (boots:width pad))
+                    (l (length s)))
+  (boots:draw pad (if (<= w l) 0 (truncate (- w l) 2))
+              y s attr))
 
 (defun dbg (&optional string &rest args)
   (if string
@@ -71,154 +84,341 @@
       (write-line s)))
   (values))
 
+(defun unlock (&rest keys)
+  (apply #'hset-insert! *unlocked* keys))
 
+(defun unlockedp (key)
+  (hset-contains-p *unlocked* key))
+
+(defun msg (string &rest args)
+  (rb-push *messages* (apply #'format nil string args)))
+
+
+;;;; Items --------------------------------------------------------------------
+;;; These classes don't represent individual items, but rather the whole
+;;; set/count/pile of a particular type of item.
+
+(defclass* item ()
+  (section
+   key
+   description
+   (owned :initform 0)))
+
+(defclass* mushroom (item)
+  ((section :initform 'mushrooms)
+   (calories :initform 100)))
+
+(defclass* tool (item)
+  ((section :initform 'tools)))
+
+(defmethod print-object ((o item) s)
+  (print-unreadable-object (o s :type t)
+    (format s "~A" (key o))))
+
+(defun make-items (class data)
+  (iterate
+    (for (key description . initargs) :in data)
+    (collect (apply #'make-instance class :key key :description description initargs))))
+
+(defun make-tools ()
+  (make-items 'tool '((trowel "Small trowel" :owned 1)
+                      (bucket "Sturdy bucket" :owned 1))))
+
+(defun make-mushrooms ()
+  (make-items 'mushroom
+              '((chantrelle "Chantrelle mushroom" :calories 60)
+                (matsutake "Matsutake mushroom" :calories 200))))
+
+
+;;;; Inventory ----------------------------------------------------------------
+(defvar *inventory* nil)
+(defvar *inventory-index* nil)
+
+(defparameter *sections* '(tools mushrooms))
+
+(defun inv-ref (section key)
+  ;; should have been a setf expander but time is short
+  (gethash key (gethash section *inventory*)))
+
+(defun inv-inc (o &optional key)
+  (if (null key)
+    (incf (owned (gethash (key o) (gethash (section o) *inventory*))) 1)
+    (incf (owned (gethash key (gethash o *inventory*))) 1)))
+
+(defun inv-dec (o &optional key)
+  (if (null key)
+    (decf (owned (gethash (key o) (gethash (section o) *inventory*))) 1)
+    (decf (owned (gethash key (gethash o *inventory*))) 1)))
+
+(defun inv-insert (&rest items)
+  (dolist (item items)
+    (setf (gethash (key item) (gethash (section item) *inventory*))
+          item)))
+
+(defun inv-section-items (section &key only-owned)
+  (iterate (for (nil item) :in-hashtable (gethash section *inventory*))
+           (when (or (not only-owned) (plusp (owned item)))
+             (collect item))))
+
+
+(defun init/inventory ()
+  (setf *inventory* (make-hash-table))
+  (dolist (s *sections*)
+    (setf (gethash s *inventory*) (make-hash-table)))
+  (apply #'inv-insert (make-mushrooms))
+  (apply #'inv-insert (make-tools))
+  (setf *inventory-index*
+        (iterate (for s :in *sections*)
+                 (for keys = (sort (alexandria:hash-table-keys (gethash s *inventory*)) #'string<))
+                 (collect (list s keys)))))
+
+
+;;;; Hunger -------------------------------------------------------------------
+(defun eat (mushroom)
+  (incf *calories* (calories mushroom))
+  (inv-dec mushroom))
+
+(defun attempt-to-eat ()
+  (let ((options (inv-section-items 'mushrooms :only-owned t)))
+    (if (null options)
+      nil
+      (progn (msg "You feel hungry.  You eat a mushroom.")
+             (eat (random-elt options #'random))))))
+
+(defun tick/hunger (delta)
+  (let ((consumed (* delta *calories-per-second*)))
+    (setf *calories* (max 0.0 (- *calories* consumed)))
+    (when (zerop *calories*)
+      (attempt-to-eat))))
 
 ;;;; Splash -------------------------------------------------------------------
-(defun draw/splash (pad)
-  (boots:draw pad 0 0 *asset/splash*))
+(defparameter *ui/splash/bg*
+  (boots:make-canvas :draw 'draw/splash/bg))
+
+(defparameter *ui/splash/card*
+  (boots:make-canvas :width (reduce #'max *asset/splash* :key #'length)
+                     :height (length *asset/splash*)
+                     :border t :margin t
+                     :fill-char #\space :fill-attr +default+
+                     :draw 'draw/splash/card))
+
+(defparameter *ui/splash*
+  (boots:pile ()
+    *ui/splash/card*
+    *ui/splash/bg*))
+
+(defun draw/splash/card (pad)
+  (iterate
+    (for y :from 0)
+    (for line :in *asset/splash*)
+    (boots:draw pad 0 y line +default+)))
+
+(defun draw/splash/bg (pad)
+  (dotimes (x (boots:width pad))
+    (dotimes (y (boots:height pad))
+      (boots:draw pad x y
+                  (losh:random-elt "\\/_-,.`" #'random)
+                  (boots:attr :fg +dark-purple+)))))
 
 (defun splash ()
   (with-ooc-colors
-    (with-ui (boots:make-canvas :width 50 :height 10 :border t :margin t
-                                :fill-char #\space
-                                :fill-attr +default+
-                                :draw #'draw/splash)
+    (with-ui *ui/splash*
       (boots:redraw)
-      (press-any-key)))
-  (chiron))
+      (press-key #\space)))
+  (game))
 
 
-;;;; Journal ------------------------------------------------------------------
-(defun draw/journal (pad)
-  (boots:draw pad 0 0 *asset/journal* +default+))
-
+;;;; Panels -------------------------------------------------------------------
+(defclass* panel ()
+  ((draw-function)
+   (selected)
+   (key)
+   (ui)))
 
-;;;; Email --------------------------------------------------------------------
-(defun draw/email (pad)
-  (boots:draw pad 0 0 "this is ur email" +default+))
-
+(defgeneric panel-name (panel))
 
 
-;;;; Chiron -------------------------------------------------------------------
-(defun human-countdown ()
-  (nest
-    (multiple-value-bind (remaining) (truncate *countdown* internal-time-units-per-second))
-    (multiple-value-bind (days remaining) (truncate remaining (* 60 60 24)))
-    (multiple-value-bind (hours remaining) (truncate remaining (* 60 60)))
-    (multiple-value-bind (mins remaining) (truncate remaining 60))
-    (multiple-value-bind (secs) (truncate remaining))
-    (format nil "~Dd ~2,'0Dh ~2,'0Dm ~2,'0Ds" days hours mins secs)))
+;;;; Forest -------------------------------------------------------------------
+(defparameter *ui/forest/forage*
+  (boots:make-canvas :height 1 :margin-bottom 1 :draw 'draw/forest/forage))
 
-(defun draw/chiron/top-bar (pad)
-  (boots:draw pad 0 0 "CHIRON" (boots:attr :fg +black+ :bg +amber+ :bold t))
-  (let ((closing (format nil "Window closes in: ~A" (human-countdown))))
-    (boots:draw pad (- (boots:width pad) (length closing)) 0
-                closing +reverse+)))
-
-(defun draw/chiron/bottom-bar (pad)
-  (boots:draw pad 0 0 "user: elf@chiron" +reverse+)
-  (draw-right pad (1- (boots:width pad)) 0
-              (format nil "(SEED ~X)" *seed*) +reverse+))
+(defparameter *ui/forest*
+  (boots:shelf (:fill-char #\space :fill-attr +default+)
+    *ui/forest/forage*))
 
 
-(defparameter *chiron/top-bar*
-  (boots:make-canvas :height 1 :margin-bottom 1
-                     :fill-char #\space
-                     :fill-attr +reverse+ :draw #'draw/chiron/top-bar))
+(defclass* forest (panel)
+  ((ui :initform *ui/forest*)
+   (key :initform 'forest)))
+
+(defmethod panel-name ((p forest))
+  "Forest")
+
+(defun draw/forest/forage (pad)
+  (when (unlockedp 'forage)
+    (boots:draw pad 0 0 "[Forage for mushrooms]" +default+)))
 
-(defparameter *chiron/bottom-bar*
-  (boots:make-canvas :height 1 :margin-top 1
-                     :fill-char #\space
-                     :fill-attr +reverse+ :draw #'draw/chiron/bottom-bar))
+(chancery:define-rule (random-forage :distribution :weighted)
+  (1 'chantrelle)
+  (1 'matsutake))
+
+
+(defun press/forage ()
+  (if (randomp *forage-chance*)
+    (progn (msg "You found a mushroom.")
+           (inv-inc 'mushrooms (random-forage)))
+    (msg "You don't find anything.")))
 
 
-(defparameter *chiron/journal* (boots:make-canvas :draw #'draw/journal))
-(defparameter *chiron/email* (boots:make-canvas :draw #'draw/email))
+;;;; Game ---------------------------------------------------------------------
+(defparameter *ui/game/top-bar*
+  (boots:shelf (:fill-char #\space :fill-attr +default+ :height 1 :border-bottom t)
+    (boots:make-canvas :fill-char #\space :fill-attr +default+
+                       :draw 'draw/game/top-bar/panels
+                       :margin-right t)
+    (boots:make-canvas :width 10
+                       :fill-char #\space :fill-attr +default+
+                       :draw 'draw/game/top-bar/time)))
 
-(defparameter *chiron* (boots:pile ()))
+(defparameter *ui/game/messages*
+  (boots:make-canvas :height 8 :border-top t
+                     :fill-char #\space :fill-attr +default+
+                     :draw 'draw/game/messages))
+
+(defparameter *ui/game/inventory*
+  (boots:make-canvas :width 35 :border-left t
+                     :fill-char #\space :fill-attr +default+
+                     :draw 'draw/game/inventory))
 
-(defmacro with-chiron-layer (widget &body body)
-  `(progn
-     (push ,widget (boots:children *chiron*))
-     (unwind-protect (progn ,@body)
-       (pop (boots:children *chiron*)))))
+(defparameter *ui/game/panel*
+  (boots:pile ()
+    (boots:make-canvas :fill-char #\? :fill-attr +default+)))
 
-(defun key-name (key)
-  (case key 
-    (#\tab "tab")
-    (t (string key))))
+(defparameter *ui/game*
+  (boots:pile (:fill-char #\space :fill-attr +default+)
+    (boots:stack ()
+      *ui/game/top-bar*
+      (boots:shelf ()
+        *ui/game/panel*
+        *ui/game/inventory*)
+      *ui/game/messages*)))
+
+
+(defun draw/game/top-bar/panels (pad)
+  (boots:draw pad 0 0
+              (iterate (for panel :in-vector *panels*)
+                       (collect (if (selected panel) +selected+ +reverse+))
+                       (collect (panel-name panel)))))
+
+(defun draw/game/top-bar/time (pad)
+  (draw-right pad (1- (boots:width pad)) 0
+              (format nil "~Ds" (truncate *elapsed* internal-time-units-per-second))
+              +default+))
 
-(defun select-box (heading options)
-  (with-chiron-layer
-      (boots:stack (:margin t :width 20 :height (+ (length options) 2) :border t
-                    :fill-char #\space :fill-attr +default+)
-        (boots:canvas (:fill-char #\space :fill-attr +default+ :height 1 :border-bottom t) (pad)
-          (boots:draw pad
-                      (truncate (- (boots:width pad) (length heading)) 2)
-                      0 heading +default+))
-        (boots:canvas (:fill-char #\space :fill-attr +default+) (pad)
-          (iterate (for y :from 0)
-                   (for (key description result) :in options)
-                   (boots:draw pad 0 y (format nil "[~A] ~A" (key-name key)
-                                               description) +default+))))
-    (third (iterate
-             (boots:redraw)
-             (for (values e m) = (boots:read-event))
-             (unless (handle-global-event e m)
-               (thereis (find-if (lambda (option)
-                                   (boots:event= option e m))
-                                 options :key #'first)))))))
+(defun draw/game/messages (pad)
+  ;; todo make reverse iteration stuff for ring buffers
+  (iterate (for i :from -1 :downto (- (rb-count *messages*)))
+           (for y :from 0)
+    (boots:draw pad 0 y (rb-ref *messages* i) +default+)))
 
-(defun select ()
-  (select-box "SELECT"
-              '((#\m "Mail" :mail)
-                (#\j "Journal" :journal)
-                (#\q "Log Out" :quit)
-                (#\tab "Nevermind" :nevermind))))
+(defun draw/game/inventory (pad)
+  (draw-center pad 0 "INVENTORY" +bold+)
+  (iterate
+    (with y = 2)
+    (with h = (boots:height pad))
+    (while (< y h))
+    (for sy = y)
+    (for (section keys) :in *inventory-index*)
+    (iterate (for key :in keys)
+             (for item = (inv-ref section key))
+             (when (plusp (owned item))
+               (incf y)
+               (boots:draw pad 0 y (format nil "~6D ~A"
+                                           (owned item)
+                                           (description item))
+                           +default+)))
+    (unless (= sy y)
+      ;; only draw section header if we drew anything else
+      (boots:draw pad 0 sy (symbol-name section) +default+)
+      (incf y 2))))
+
 
-(defun chiron ()
-  (with-ui *chiron*
-    (with-chiron-layer (boots:stack (:fill-char #\space :fill-attr +default+)
-                         *chiron/top-bar*
-                         *chiron/journal*
-                         *chiron/bottom-bar*)
-      (iterate
-        (timing real-time :per-iteration-into delta)
-        (decf *countdown* delta)
-        (boots:redraw)
-        (event-case (boots:read-event-no-hang)
-          (#\tab (dbg "~S" (select)))
-          (t nil))))))
+(defmacro with-layer (ui &body body)
+  `(progn (push ,ui (boots:children *ui/game*))
+     (unwind-protect (progn ,@body)
+       (pop (boots:children *ui/game*)))))
+
+(defun tick (delta)
+  (tick/hunger delta))
+
+(defun game ()
+  (with-ui *ui/game*
+    (iterate
+      (timing real-time :per-iteration-into delta)
+      (incf *elapsed* delta)
+      (tick (/ (float delta 1.0d0) internal-time-units-per-second))
+      (setf (first (boots:children *ui/game/panel*))
+            (ui (find-if #'selected *panels*)))
+      (boots:redraw)
+      (event-case (boots:read-event-no-hang)
+        (nil (boots:wait 1/30))
+        (#\newline (press/forage))
+        (#\tab (msg "TODO"))
+        (t nil)))))
 
 
 ;;;; Pause --------------------------------------------------------------------
+(defparameter *ui/pause*
+  (boots:make-canvas :width 30 :height 4 :border t :margin t
+                     :fill-char #\space :fill-attr +default+
+                     :draw 'draw/pause))
+
 (defun draw/pause (pad)
   (boots:draw pad 0 0 "Paused" +ooc+)
   (boots:draw pad 0 2 "[R]esume" +ooc+)
   (boots:draw pad 0 3 "[Q]uit Game" +ooc+))
 
 (defun pause ()
-  (with-ooc-colors
-    (with-ui (boots:make-canvas :width 30 :height 10 :border t :margin t
-                                :fill-char #\space
-                                :fill-attr +ooc+
-                                :draw #'draw/pause)
-      (loop (boots:redraw)
-            (multiple-value-bind (e m) (boots:read-event)
-              (boots:event-case (values e m)
-                ((#\r #\esc) (return-from pause))
-                (#\q (throw 'quit nil))
-                (t (setf *event* e *mods* m))))))))
+  (with-ui *ui/pause*
+    (loop (boots:redraw)
+          (multiple-value-bind (e m) (boots:read-event)
+            (boots:event-case (values e m)
+              ((#\r #\esc) (return-from pause))
+              (#\q (throw 'quit nil))
+              (t (setf *event* e *mods* m)))))))
+
+
+;;;; Help --------------------------------------------------------------------
+(defparameter *ui/help*
+  (boots:make-canvas :width 50 :height (+ 3 (length *asset/help*))
+                     :border t :margin t
+                     :fill-char #\space :fill-attr +default+
+                     :draw 'draw/help))
+
+(defun draw/help (pad)
+  (boots:draw pad 0 0 "HELP" +default+)
+  (iterate (for y :from 2)
+           (for line :in *asset/help*)
+           (boots:draw pad 0 y line +default+)))
+
+(defun help ()
+  (with-ui *ui/help*
+    (boots:redraw)
+    (press-key t)))
 
 
 ;;;; Main ---------------------------------------------------------------------
 (defun init ()
   (rb-clear *debug*)
+  (rb-clear *messages*)
+  (init/inventory)
   (setf *seed* (cl:random (expt 2 60) (cl:make-random-state))
         *pcg* (pcg:make-pcg :seed *seed*)
-        *countdown* (* internal-time-units-per-second
-                       (+ (* 60 60 24 15)
-                          (random 0 (* 60 60 4))))))
+        *elapsed* 0
+        *calories* (* 1.0  10)
+        *panels* (vector (make-instance 'forest :selected t))
+        *unlocked* (make-hash-set :initial-contents '(forest forage)))
+  (values))
 
 (defun run ()
   (init)
@@ -235,7 +435,7 @@
   (run))
 
 (defun build ()
-  (sb-ext:save-lisp-and-die "build/dark"
+  (sb-ext:save-lisp-and-die "build/mycelium"
     :executable t
     :save-runtime-options t
-    :toplevel #'dark:toplevel))
+    :toplevel #'mycelium:toplevel))
--- a/src/package.lisp	Tue Apr 20 15:13:04 2021 -0400
+++ b/src/package.lisp	Tue Apr 20 18:01:32 2021 -0400
@@ -1,4 +1,4 @@
-(defpackage :dark
+(defpackage :mycelium
   (:use :cl :iterate :losh)
   (:shadow :random)
   (:export :toplevel :build))