diff --git a/naledi.asd b/naledi.asd index 73bb35e..f3174ad 100644 --- a/naledi.asd +++ b/naledi.asd @@ -30,7 +30,8 @@ (:file "item-methods") (:file "world") (:file "server") - (:file "client"))) + (:file "client") + (:file "crt-ext"))) (:module "content" :components ((:file "animals") diff --git a/naledi.asd b/naledi.asd index 73bb35e..f3174ad 100644 --- a/naledi.asd +++ b/naledi.asd @@ -30,7 +30,8 @@ (:file "item-methods") (:file "world") (:file "server") - (:file "client"))) + (:file "client") + (:file "crt-ext"))) (:module "content" :components ((:file "animals") diff --git a/naledi.lisp b/naledi.lisp index 942ef18..d469630 100644 --- a/naledi.lisp +++ b/naledi.lisp @@ -64,23 +64,27 @@ ((:up :down) (croatoan:update-menu mw event) (croatoan:draw-menu mw)) - (#\newline + (#\newline ;;XXX this clause is too long + ;;FIXME starting a local game doesn't work anymore... (cond ((equalp (croatoan:current-item mw) "Start a local game") ;;TODO choose world size (start-server) (setf *host* (first *defaulthost*) *port* (second *defaulthost*)) - ;;TODO check whether server is up! - (sleep 1) ;;give the server time to start + ;;give the server time to start + ;(while (not (runningp)) ;;TODO replace with `loop' + (sleep 1) (connect-server)) ((equalp (croatoan:current-item mw) "Connect to a remote server") - (setf *host* (query-user scr "Server IP/URL:" T)) - (setf *port* (read-from-string - (query-user scr "Server port:" T))) + (setf *host* (query-user scr "Server IP/URL:" :cls T)) + (setf *port* + (read-from-string + (query-user scr "Server port:" + :default (second *defaulthost*) :cls T))) (if (user-confirm-p scr - (format nil "Connect to ~A:~S?" *host* *port*) - T) + (format nil "Connect to ~A:~S?" *host* *port*) + T) (connect-server) (start-or-connect-to-server scr))) (return-from croatoan:event-case)))))) @@ -207,6 +211,7 @@ (defun message-window () "Return a dialog window with the last game messages." ;;TODO complete + ;;XXX use `user-inform' instead of dialog-window? (make-instance 'croatoan:dialog-window :input-blocking t :items (break-lines @@ -222,63 +227,6 @@ :message-height 2 :message-text "Press b to go back."))) ;;:event-handlers '((#\b #'exit-event-loop))))) - -(defun query-user (scr msg &optional (cls NIL) (val-width 30)) - "Display a popup asking the user to enter a value, then return that value" - ;;XXX I found `field/form' to complicated to use (:form-default-keymap - ;; doesn't seem to work), so I hacked up my own alternative - (when cls (croatoan:clear scr)) - (croatoan:refresh scr) - (let* ((x0 (- (halve (croatoan:.width scr)) (+ 5 (halve val-width)))) - (y0 (- (halve (croatoan:.height scr)) 3)) - (inputwin (make-instance 'croatoan:window :position (list y0 x0) - :width (+ 10 val-width) :height 6))) - (setf (croatoan:.visible inputwin) t) - (croatoan:box inputwin) - (croatoan:add-string inputwin msg :y 2 :x 4) - (croatoan:add-string inputwin ">>> " :y 3 :x 4) - (croatoan:move inputwin 3 8) - (croatoan:refresh inputwin) - (croatoan:event-case (scr event) - ((nil) nil) - (:backspace - (when (< 8 (croatoan:.cursor-position-x inputwin)) - (croatoan:move inputwin 0 -1 :relative t) - (croatoan:add-char inputwin #\space) - (croatoan:move inputwin 0 -1 :relative t) - (croatoan:refresh inputwin))) - (#\newline - (return-from croatoan:event-case - (trim-whitespace - (croatoan:extract-string inputwin - :y 3 :x 8 :n val-width)))) - (otherwise - (when (and (characterp event) - (> (+ 8 val-width) - (croatoan:.cursor-position-x inputwin))) - (croatoan:add-char inputwin event - :color-pair '(:blue :black)) - (croatoan:refresh inputwin)))))) - -(defun user-confirm-p (scr msg &optional (cls NIL)) - "Ask the user to confirm (Yes/No) a message." - (when cls (croatoan:clear scr)) - (croatoan:refresh scr) - (let ((dw (make-instance 'croatoan:dialog-window - :center t :border t :width (max 20 (+ 4 (length msg))) - :max-item-length (max 4 (halve (length msg))) - :input-blocking t :layout '(1 2) - :message-height 1 :message-text msg - :items '(" Yes" " No")))) - (croatoan:draw-menu dw) - (croatoan:event-case (scr event) - ((nil) nil) - ((:left :right) - (croatoan:update-menu dw event) - (croatoan:draw-menu dw)) - (#\newline - (return-from croatoan:event-case - (equalp (croatoan:current-item dw) " Yes")))))) (defun process-command (event) diff --git a/naledi.asd b/naledi.asd index 73bb35e..f3174ad 100644 --- a/naledi.asd +++ b/naledi.asd @@ -30,7 +30,8 @@ (:file "item-methods") (:file "world") (:file "server") - (:file "client"))) + (:file "client") + (:file "crt-ext"))) (:module "content" :components ((:file "animals") diff --git a/naledi.lisp b/naledi.lisp index 942ef18..d469630 100644 --- a/naledi.lisp +++ b/naledi.lisp @@ -64,23 +64,27 @@ ((:up :down) (croatoan:update-menu mw event) (croatoan:draw-menu mw)) - (#\newline + (#\newline ;;XXX this clause is too long + ;;FIXME starting a local game doesn't work anymore... (cond ((equalp (croatoan:current-item mw) "Start a local game") ;;TODO choose world size (start-server) (setf *host* (first *defaulthost*) *port* (second *defaulthost*)) - ;;TODO check whether server is up! - (sleep 1) ;;give the server time to start + ;;give the server time to start + ;(while (not (runningp)) ;;TODO replace with `loop' + (sleep 1) (connect-server)) ((equalp (croatoan:current-item mw) "Connect to a remote server") - (setf *host* (query-user scr "Server IP/URL:" T)) - (setf *port* (read-from-string - (query-user scr "Server port:" T))) + (setf *host* (query-user scr "Server IP/URL:" :cls T)) + (setf *port* + (read-from-string + (query-user scr "Server port:" + :default (second *defaulthost*) :cls T))) (if (user-confirm-p scr - (format nil "Connect to ~A:~S?" *host* *port*) - T) + (format nil "Connect to ~A:~S?" *host* *port*) + T) (connect-server) (start-or-connect-to-server scr))) (return-from croatoan:event-case)))))) @@ -207,6 +211,7 @@ (defun message-window () "Return a dialog window with the last game messages." ;;TODO complete + ;;XXX use `user-inform' instead of dialog-window? (make-instance 'croatoan:dialog-window :input-blocking t :items (break-lines @@ -222,63 +227,6 @@ :message-height 2 :message-text "Press b to go back."))) ;;:event-handlers '((#\b #'exit-event-loop))))) - -(defun query-user (scr msg &optional (cls NIL) (val-width 30)) - "Display a popup asking the user to enter a value, then return that value" - ;;XXX I found `field/form' to complicated to use (:form-default-keymap - ;; doesn't seem to work), so I hacked up my own alternative - (when cls (croatoan:clear scr)) - (croatoan:refresh scr) - (let* ((x0 (- (halve (croatoan:.width scr)) (+ 5 (halve val-width)))) - (y0 (- (halve (croatoan:.height scr)) 3)) - (inputwin (make-instance 'croatoan:window :position (list y0 x0) - :width (+ 10 val-width) :height 6))) - (setf (croatoan:.visible inputwin) t) - (croatoan:box inputwin) - (croatoan:add-string inputwin msg :y 2 :x 4) - (croatoan:add-string inputwin ">>> " :y 3 :x 4) - (croatoan:move inputwin 3 8) - (croatoan:refresh inputwin) - (croatoan:event-case (scr event) - ((nil) nil) - (:backspace - (when (< 8 (croatoan:.cursor-position-x inputwin)) - (croatoan:move inputwin 0 -1 :relative t) - (croatoan:add-char inputwin #\space) - (croatoan:move inputwin 0 -1 :relative t) - (croatoan:refresh inputwin))) - (#\newline - (return-from croatoan:event-case - (trim-whitespace - (croatoan:extract-string inputwin - :y 3 :x 8 :n val-width)))) - (otherwise - (when (and (characterp event) - (> (+ 8 val-width) - (croatoan:.cursor-position-x inputwin))) - (croatoan:add-char inputwin event - :color-pair '(:blue :black)) - (croatoan:refresh inputwin)))))) - -(defun user-confirm-p (scr msg &optional (cls NIL)) - "Ask the user to confirm (Yes/No) a message." - (when cls (croatoan:clear scr)) - (croatoan:refresh scr) - (let ((dw (make-instance 'croatoan:dialog-window - :center t :border t :width (max 20 (+ 4 (length msg))) - :max-item-length (max 4 (halve (length msg))) - :input-blocking t :layout '(1 2) - :message-height 1 :message-text msg - :items '(" Yes" " No")))) - (croatoan:draw-menu dw) - (croatoan:event-case (scr event) - ((nil) nil) - ((:left :right) - (croatoan:update-menu dw event) - (croatoan:draw-menu dw)) - (#\newline - (return-from croatoan:event-case - (equalp (croatoan:current-item dw) " Yes")))))) (defun process-command (event) diff --git a/package.lisp b/package.lisp index 5f3c2b7..26a0688 100644 --- a/package.lisp +++ b/package.lisp @@ -30,5 +30,8 @@ *host* *port*)) +;;set debug level during development +(declaim (optimize (debug 3))) + ;;convenience function (defun start () (nya:start-game)) diff --git a/naledi.asd b/naledi.asd index 73bb35e..f3174ad 100644 --- a/naledi.asd +++ b/naledi.asd @@ -30,7 +30,8 @@ (:file "item-methods") (:file "world") (:file "server") - (:file "client"))) + (:file "client") + (:file "crt-ext"))) (:module "content" :components ((:file "animals") diff --git a/naledi.lisp b/naledi.lisp index 942ef18..d469630 100644 --- a/naledi.lisp +++ b/naledi.lisp @@ -64,23 +64,27 @@ ((:up :down) (croatoan:update-menu mw event) (croatoan:draw-menu mw)) - (#\newline + (#\newline ;;XXX this clause is too long + ;;FIXME starting a local game doesn't work anymore... (cond ((equalp (croatoan:current-item mw) "Start a local game") ;;TODO choose world size (start-server) (setf *host* (first *defaulthost*) *port* (second *defaulthost*)) - ;;TODO check whether server is up! - (sleep 1) ;;give the server time to start + ;;give the server time to start + ;(while (not (runningp)) ;;TODO replace with `loop' + (sleep 1) (connect-server)) ((equalp (croatoan:current-item mw) "Connect to a remote server") - (setf *host* (query-user scr "Server IP/URL:" T)) - (setf *port* (read-from-string - (query-user scr "Server port:" T))) + (setf *host* (query-user scr "Server IP/URL:" :cls T)) + (setf *port* + (read-from-string + (query-user scr "Server port:" + :default (second *defaulthost*) :cls T))) (if (user-confirm-p scr - (format nil "Connect to ~A:~S?" *host* *port*) - T) + (format nil "Connect to ~A:~S?" *host* *port*) + T) (connect-server) (start-or-connect-to-server scr))) (return-from croatoan:event-case)))))) @@ -207,6 +211,7 @@ (defun message-window () "Return a dialog window with the last game messages." ;;TODO complete + ;;XXX use `user-inform' instead of dialog-window? (make-instance 'croatoan:dialog-window :input-blocking t :items (break-lines @@ -222,63 +227,6 @@ :message-height 2 :message-text "Press b to go back."))) ;;:event-handlers '((#\b #'exit-event-loop))))) - -(defun query-user (scr msg &optional (cls NIL) (val-width 30)) - "Display a popup asking the user to enter a value, then return that value" - ;;XXX I found `field/form' to complicated to use (:form-default-keymap - ;; doesn't seem to work), so I hacked up my own alternative - (when cls (croatoan:clear scr)) - (croatoan:refresh scr) - (let* ((x0 (- (halve (croatoan:.width scr)) (+ 5 (halve val-width)))) - (y0 (- (halve (croatoan:.height scr)) 3)) - (inputwin (make-instance 'croatoan:window :position (list y0 x0) - :width (+ 10 val-width) :height 6))) - (setf (croatoan:.visible inputwin) t) - (croatoan:box inputwin) - (croatoan:add-string inputwin msg :y 2 :x 4) - (croatoan:add-string inputwin ">>> " :y 3 :x 4) - (croatoan:move inputwin 3 8) - (croatoan:refresh inputwin) - (croatoan:event-case (scr event) - ((nil) nil) - (:backspace - (when (< 8 (croatoan:.cursor-position-x inputwin)) - (croatoan:move inputwin 0 -1 :relative t) - (croatoan:add-char inputwin #\space) - (croatoan:move inputwin 0 -1 :relative t) - (croatoan:refresh inputwin))) - (#\newline - (return-from croatoan:event-case - (trim-whitespace - (croatoan:extract-string inputwin - :y 3 :x 8 :n val-width)))) - (otherwise - (when (and (characterp event) - (> (+ 8 val-width) - (croatoan:.cursor-position-x inputwin))) - (croatoan:add-char inputwin event - :color-pair '(:blue :black)) - (croatoan:refresh inputwin)))))) - -(defun user-confirm-p (scr msg &optional (cls NIL)) - "Ask the user to confirm (Yes/No) a message." - (when cls (croatoan:clear scr)) - (croatoan:refresh scr) - (let ((dw (make-instance 'croatoan:dialog-window - :center t :border t :width (max 20 (+ 4 (length msg))) - :max-item-length (max 4 (halve (length msg))) - :input-blocking t :layout '(1 2) - :message-height 1 :message-text msg - :items '(" Yes" " No")))) - (croatoan:draw-menu dw) - (croatoan:event-case (scr event) - ((nil) nil) - ((:left :right) - (croatoan:update-menu dw event) - (croatoan:draw-menu dw)) - (#\newline - (return-from croatoan:event-case - (equalp (croatoan:current-item dw) " Yes")))))) (defun process-command (event) diff --git a/package.lisp b/package.lisp index 5f3c2b7..26a0688 100644 --- a/package.lisp +++ b/package.lisp @@ -30,5 +30,8 @@ *host* *port*)) +;;set debug level during development +(declaim (optimize (debug 3))) + ;;convenience function (defun start () (nya:start-game)) diff --git a/src/crt-ext.lisp b/src/crt-ext.lisp new file mode 100644 index 0000000..ff49a56 --- /dev/null +++ b/src/crt-ext.lisp @@ -0,0 +1,76 @@ +;;;; +;;;; Naledi ya Africa ("Star of Africa") is an ncurses-based survival game +;;;; set in Africa. +;;;; +;;;; This file holds extension functions for croatoan. +;;;; +;;;; (c) 2018 Daniel Vedder, MIT license +;;;; + +(in-package :naledi-ya-africa) + +(defun query-user (scr msg &key (cls NIL) (val-width 30) (default "")) + "Display a popup asking the user to enter a value, then return that value" + ;; I found croatoan's field/form to complicated to use (:form-default-keymap + ;; doesn't seem to work), so I hacked up my own alternative + ;; TODO display additional lines of text + (when cls (croatoan:clear scr)) + (croatoan:refresh scr) + (let* ((x0 (- (halve (croatoan:.width scr)) (+ 5 (halve val-width)))) + (y0 (- (halve (croatoan:.height scr)) 3)) + (inputwin (make-instance 'croatoan:window :position (list y0 x0) + :width (+ 10 val-width) :height 6))) + (setf (croatoan:.visible inputwin) t) + (croatoan:box inputwin) + (croatoan:add-string inputwin msg :y 2 :x 4) + (croatoan:add-string inputwin ">>> " :y 3 :x 4) + (croatoan:add-string inputwin (to-string default) :y 3 :x 8 + :color-pair '(:blue :black)) + (croatoan:move inputwin 3 (+ 8 (length (to-string default)))) + (croatoan:refresh inputwin) + (croatoan:event-case (scr event) + ((nil) nil) + (:backspace + (when (< 8 (croatoan:.cursor-position-x inputwin)) + (croatoan:move inputwin 0 -1 :relative t) + (croatoan:add-char inputwin #\space) + (croatoan:move inputwin 0 -1 :relative t) + (croatoan:refresh inputwin))) + (#\newline + (return-from croatoan:event-case + (trim-whitespace + (croatoan:extract-string inputwin + :y 3 :x 8 :n val-width)))) + (otherwise + (when (and (characterp event) + (> (+ 8 val-width) + (croatoan:.cursor-position-x inputwin))) + (croatoan:add-char inputwin event + :color-pair '(:blue :black)) + (croatoan:refresh inputwin)))))) + +(defun user-confirm-p (scr msg &optional (cls NIL)) + "Ask the user to confirm (Yes/No) a message." + (when cls (croatoan:clear scr)) + (croatoan:refresh scr) + (let ((dw (make-instance 'croatoan:dialog-window + :center t :border t :width (max 20 (+ 4 (length msg))) + :max-item-length (max 4 (halve (length msg))) + :input-blocking t :layout '(1 2) + :message-height 1 :message-text msg + :items '(" Yes" " No")))) + (croatoan:draw-menu dw) + (croatoan:event-case (scr event) + ((nil) nil) + ((:left :right) + (croatoan:update-menu dw event) + (croatoan:draw-menu dw)) + (#\newline + (return-from croatoan:event-case + (equalp (croatoan:current-item dw) " Yes")))))) + +(defun inform-user (scr msg) + "Display an informational message to the user" + ;;TODO + ) + diff --git a/naledi.asd b/naledi.asd index 73bb35e..f3174ad 100644 --- a/naledi.asd +++ b/naledi.asd @@ -30,7 +30,8 @@ (:file "item-methods") (:file "world") (:file "server") - (:file "client"))) + (:file "client") + (:file "crt-ext"))) (:module "content" :components ((:file "animals") diff --git a/naledi.lisp b/naledi.lisp index 942ef18..d469630 100644 --- a/naledi.lisp +++ b/naledi.lisp @@ -64,23 +64,27 @@ ((:up :down) (croatoan:update-menu mw event) (croatoan:draw-menu mw)) - (#\newline + (#\newline ;;XXX this clause is too long + ;;FIXME starting a local game doesn't work anymore... (cond ((equalp (croatoan:current-item mw) "Start a local game") ;;TODO choose world size (start-server) (setf *host* (first *defaulthost*) *port* (second *defaulthost*)) - ;;TODO check whether server is up! - (sleep 1) ;;give the server time to start + ;;give the server time to start + ;(while (not (runningp)) ;;TODO replace with `loop' + (sleep 1) (connect-server)) ((equalp (croatoan:current-item mw) "Connect to a remote server") - (setf *host* (query-user scr "Server IP/URL:" T)) - (setf *port* (read-from-string - (query-user scr "Server port:" T))) + (setf *host* (query-user scr "Server IP/URL:" :cls T)) + (setf *port* + (read-from-string + (query-user scr "Server port:" + :default (second *defaulthost*) :cls T))) (if (user-confirm-p scr - (format nil "Connect to ~A:~S?" *host* *port*) - T) + (format nil "Connect to ~A:~S?" *host* *port*) + T) (connect-server) (start-or-connect-to-server scr))) (return-from croatoan:event-case)))))) @@ -207,6 +211,7 @@ (defun message-window () "Return a dialog window with the last game messages." ;;TODO complete + ;;XXX use `user-inform' instead of dialog-window? (make-instance 'croatoan:dialog-window :input-blocking t :items (break-lines @@ -222,63 +227,6 @@ :message-height 2 :message-text "Press b to go back."))) ;;:event-handlers '((#\b #'exit-event-loop))))) - -(defun query-user (scr msg &optional (cls NIL) (val-width 30)) - "Display a popup asking the user to enter a value, then return that value" - ;;XXX I found `field/form' to complicated to use (:form-default-keymap - ;; doesn't seem to work), so I hacked up my own alternative - (when cls (croatoan:clear scr)) - (croatoan:refresh scr) - (let* ((x0 (- (halve (croatoan:.width scr)) (+ 5 (halve val-width)))) - (y0 (- (halve (croatoan:.height scr)) 3)) - (inputwin (make-instance 'croatoan:window :position (list y0 x0) - :width (+ 10 val-width) :height 6))) - (setf (croatoan:.visible inputwin) t) - (croatoan:box inputwin) - (croatoan:add-string inputwin msg :y 2 :x 4) - (croatoan:add-string inputwin ">>> " :y 3 :x 4) - (croatoan:move inputwin 3 8) - (croatoan:refresh inputwin) - (croatoan:event-case (scr event) - ((nil) nil) - (:backspace - (when (< 8 (croatoan:.cursor-position-x inputwin)) - (croatoan:move inputwin 0 -1 :relative t) - (croatoan:add-char inputwin #\space) - (croatoan:move inputwin 0 -1 :relative t) - (croatoan:refresh inputwin))) - (#\newline - (return-from croatoan:event-case - (trim-whitespace - (croatoan:extract-string inputwin - :y 3 :x 8 :n val-width)))) - (otherwise - (when (and (characterp event) - (> (+ 8 val-width) - (croatoan:.cursor-position-x inputwin))) - (croatoan:add-char inputwin event - :color-pair '(:blue :black)) - (croatoan:refresh inputwin)))))) - -(defun user-confirm-p (scr msg &optional (cls NIL)) - "Ask the user to confirm (Yes/No) a message." - (when cls (croatoan:clear scr)) - (croatoan:refresh scr) - (let ((dw (make-instance 'croatoan:dialog-window - :center t :border t :width (max 20 (+ 4 (length msg))) - :max-item-length (max 4 (halve (length msg))) - :input-blocking t :layout '(1 2) - :message-height 1 :message-text msg - :items '(" Yes" " No")))) - (croatoan:draw-menu dw) - (croatoan:event-case (scr event) - ((nil) nil) - ((:left :right) - (croatoan:update-menu dw event) - (croatoan:draw-menu dw)) - (#\newline - (return-from croatoan:event-case - (equalp (croatoan:current-item dw) " Yes")))))) (defun process-command (event) diff --git a/package.lisp b/package.lisp index 5f3c2b7..26a0688 100644 --- a/package.lisp +++ b/package.lisp @@ -30,5 +30,8 @@ *host* *port*)) +;;set debug level during development +(declaim (optimize (debug 3))) + ;;convenience function (defun start () (nya:start-game)) diff --git a/src/crt-ext.lisp b/src/crt-ext.lisp new file mode 100644 index 0000000..ff49a56 --- /dev/null +++ b/src/crt-ext.lisp @@ -0,0 +1,76 @@ +;;;; +;;;; Naledi ya Africa ("Star of Africa") is an ncurses-based survival game +;;;; set in Africa. +;;;; +;;;; This file holds extension functions for croatoan. +;;;; +;;;; (c) 2018 Daniel Vedder, MIT license +;;;; + +(in-package :naledi-ya-africa) + +(defun query-user (scr msg &key (cls NIL) (val-width 30) (default "")) + "Display a popup asking the user to enter a value, then return that value" + ;; I found croatoan's field/form to complicated to use (:form-default-keymap + ;; doesn't seem to work), so I hacked up my own alternative + ;; TODO display additional lines of text + (when cls (croatoan:clear scr)) + (croatoan:refresh scr) + (let* ((x0 (- (halve (croatoan:.width scr)) (+ 5 (halve val-width)))) + (y0 (- (halve (croatoan:.height scr)) 3)) + (inputwin (make-instance 'croatoan:window :position (list y0 x0) + :width (+ 10 val-width) :height 6))) + (setf (croatoan:.visible inputwin) t) + (croatoan:box inputwin) + (croatoan:add-string inputwin msg :y 2 :x 4) + (croatoan:add-string inputwin ">>> " :y 3 :x 4) + (croatoan:add-string inputwin (to-string default) :y 3 :x 8 + :color-pair '(:blue :black)) + (croatoan:move inputwin 3 (+ 8 (length (to-string default)))) + (croatoan:refresh inputwin) + (croatoan:event-case (scr event) + ((nil) nil) + (:backspace + (when (< 8 (croatoan:.cursor-position-x inputwin)) + (croatoan:move inputwin 0 -1 :relative t) + (croatoan:add-char inputwin #\space) + (croatoan:move inputwin 0 -1 :relative t) + (croatoan:refresh inputwin))) + (#\newline + (return-from croatoan:event-case + (trim-whitespace + (croatoan:extract-string inputwin + :y 3 :x 8 :n val-width)))) + (otherwise + (when (and (characterp event) + (> (+ 8 val-width) + (croatoan:.cursor-position-x inputwin))) + (croatoan:add-char inputwin event + :color-pair '(:blue :black)) + (croatoan:refresh inputwin)))))) + +(defun user-confirm-p (scr msg &optional (cls NIL)) + "Ask the user to confirm (Yes/No) a message." + (when cls (croatoan:clear scr)) + (croatoan:refresh scr) + (let ((dw (make-instance 'croatoan:dialog-window + :center t :border t :width (max 20 (+ 4 (length msg))) + :max-item-length (max 4 (halve (length msg))) + :input-blocking t :layout '(1 2) + :message-height 1 :message-text msg + :items '(" Yes" " No")))) + (croatoan:draw-menu dw) + (croatoan:event-case (scr event) + ((nil) nil) + ((:left :right) + (croatoan:update-menu dw event) + (croatoan:draw-menu dw)) + (#\newline + (return-from croatoan:event-case + (equalp (croatoan:current-item dw) " Yes")))))) + +(defun inform-user (scr msg) + "Display an informational message to the user" + ;;TODO + ) + diff --git a/src/server.lisp b/src/server.lisp index 00f9a5a..aec9fb8 100644 --- a/src/server.lisp +++ b/src/server.lisp @@ -7,12 +7,12 @@ ;;;; (c) 2018 Daniel Vedder, MIT license ;;;; -(in-package :naledi-ya-africa) - ;;XXX utility function during development, remove later (defun dt (&optional (n 0)) (bt:destroy-thread (nth n (bt:all-threads)))) +(in-package :naledi-ya-africa) + ;; TODO save and load functions ;; XXX Will probably require `make-load-form-saving-slots' @@ -50,7 +50,7 @@ (defun start-server (&optional (force T)) "Start the game server" - ;;TODO cannot restart -> ADDRESS-IN-USE ERROR + ;;FIXME cannot restart -> ADDRESS-IN-USE ERROR ;; -> comes from not closing connections properly? ;;FIXME causes Naledi to hang on `q' -> Is this really the problem? ;;XXX change force back to NIL? @@ -59,7 +59,7 @@ (reset-world-age)) (unless (or world-thread server-thread) (init-world) - (setf running T) + (setf running T) (setf world-thread (bt:make-thread #'update-loop :name "world-thread")) (setf server-thread diff --git a/naledi.asd b/naledi.asd index 73bb35e..f3174ad 100644 --- a/naledi.asd +++ b/naledi.asd @@ -30,7 +30,8 @@ (:file "item-methods") (:file "world") (:file "server") - (:file "client"))) + (:file "client") + (:file "crt-ext"))) (:module "content" :components ((:file "animals") diff --git a/naledi.lisp b/naledi.lisp index 942ef18..d469630 100644 --- a/naledi.lisp +++ b/naledi.lisp @@ -64,23 +64,27 @@ ((:up :down) (croatoan:update-menu mw event) (croatoan:draw-menu mw)) - (#\newline + (#\newline ;;XXX this clause is too long + ;;FIXME starting a local game doesn't work anymore... (cond ((equalp (croatoan:current-item mw) "Start a local game") ;;TODO choose world size (start-server) (setf *host* (first *defaulthost*) *port* (second *defaulthost*)) - ;;TODO check whether server is up! - (sleep 1) ;;give the server time to start + ;;give the server time to start + ;(while (not (runningp)) ;;TODO replace with `loop' + (sleep 1) (connect-server)) ((equalp (croatoan:current-item mw) "Connect to a remote server") - (setf *host* (query-user scr "Server IP/URL:" T)) - (setf *port* (read-from-string - (query-user scr "Server port:" T))) + (setf *host* (query-user scr "Server IP/URL:" :cls T)) + (setf *port* + (read-from-string + (query-user scr "Server port:" + :default (second *defaulthost*) :cls T))) (if (user-confirm-p scr - (format nil "Connect to ~A:~S?" *host* *port*) - T) + (format nil "Connect to ~A:~S?" *host* *port*) + T) (connect-server) (start-or-connect-to-server scr))) (return-from croatoan:event-case)))))) @@ -207,6 +211,7 @@ (defun message-window () "Return a dialog window with the last game messages." ;;TODO complete + ;;XXX use `user-inform' instead of dialog-window? (make-instance 'croatoan:dialog-window :input-blocking t :items (break-lines @@ -222,63 +227,6 @@ :message-height 2 :message-text "Press b to go back."))) ;;:event-handlers '((#\b #'exit-event-loop))))) - -(defun query-user (scr msg &optional (cls NIL) (val-width 30)) - "Display a popup asking the user to enter a value, then return that value" - ;;XXX I found `field/form' to complicated to use (:form-default-keymap - ;; doesn't seem to work), so I hacked up my own alternative - (when cls (croatoan:clear scr)) - (croatoan:refresh scr) - (let* ((x0 (- (halve (croatoan:.width scr)) (+ 5 (halve val-width)))) - (y0 (- (halve (croatoan:.height scr)) 3)) - (inputwin (make-instance 'croatoan:window :position (list y0 x0) - :width (+ 10 val-width) :height 6))) - (setf (croatoan:.visible inputwin) t) - (croatoan:box inputwin) - (croatoan:add-string inputwin msg :y 2 :x 4) - (croatoan:add-string inputwin ">>> " :y 3 :x 4) - (croatoan:move inputwin 3 8) - (croatoan:refresh inputwin) - (croatoan:event-case (scr event) - ((nil) nil) - (:backspace - (when (< 8 (croatoan:.cursor-position-x inputwin)) - (croatoan:move inputwin 0 -1 :relative t) - (croatoan:add-char inputwin #\space) - (croatoan:move inputwin 0 -1 :relative t) - (croatoan:refresh inputwin))) - (#\newline - (return-from croatoan:event-case - (trim-whitespace - (croatoan:extract-string inputwin - :y 3 :x 8 :n val-width)))) - (otherwise - (when (and (characterp event) - (> (+ 8 val-width) - (croatoan:.cursor-position-x inputwin))) - (croatoan:add-char inputwin event - :color-pair '(:blue :black)) - (croatoan:refresh inputwin)))))) - -(defun user-confirm-p (scr msg &optional (cls NIL)) - "Ask the user to confirm (Yes/No) a message." - (when cls (croatoan:clear scr)) - (croatoan:refresh scr) - (let ((dw (make-instance 'croatoan:dialog-window - :center t :border t :width (max 20 (+ 4 (length msg))) - :max-item-length (max 4 (halve (length msg))) - :input-blocking t :layout '(1 2) - :message-height 1 :message-text msg - :items '(" Yes" " No")))) - (croatoan:draw-menu dw) - (croatoan:event-case (scr event) - ((nil) nil) - ((:left :right) - (croatoan:update-menu dw event) - (croatoan:draw-menu dw)) - (#\newline - (return-from croatoan:event-case - (equalp (croatoan:current-item dw) " Yes")))))) (defun process-command (event) diff --git a/package.lisp b/package.lisp index 5f3c2b7..26a0688 100644 --- a/package.lisp +++ b/package.lisp @@ -30,5 +30,8 @@ *host* *port*)) +;;set debug level during development +(declaim (optimize (debug 3))) + ;;convenience function (defun start () (nya:start-game)) diff --git a/src/crt-ext.lisp b/src/crt-ext.lisp new file mode 100644 index 0000000..ff49a56 --- /dev/null +++ b/src/crt-ext.lisp @@ -0,0 +1,76 @@ +;;;; +;;;; Naledi ya Africa ("Star of Africa") is an ncurses-based survival game +;;;; set in Africa. +;;;; +;;;; This file holds extension functions for croatoan. +;;;; +;;;; (c) 2018 Daniel Vedder, MIT license +;;;; + +(in-package :naledi-ya-africa) + +(defun query-user (scr msg &key (cls NIL) (val-width 30) (default "")) + "Display a popup asking the user to enter a value, then return that value" + ;; I found croatoan's field/form to complicated to use (:form-default-keymap + ;; doesn't seem to work), so I hacked up my own alternative + ;; TODO display additional lines of text + (when cls (croatoan:clear scr)) + (croatoan:refresh scr) + (let* ((x0 (- (halve (croatoan:.width scr)) (+ 5 (halve val-width)))) + (y0 (- (halve (croatoan:.height scr)) 3)) + (inputwin (make-instance 'croatoan:window :position (list y0 x0) + :width (+ 10 val-width) :height 6))) + (setf (croatoan:.visible inputwin) t) + (croatoan:box inputwin) + (croatoan:add-string inputwin msg :y 2 :x 4) + (croatoan:add-string inputwin ">>> " :y 3 :x 4) + (croatoan:add-string inputwin (to-string default) :y 3 :x 8 + :color-pair '(:blue :black)) + (croatoan:move inputwin 3 (+ 8 (length (to-string default)))) + (croatoan:refresh inputwin) + (croatoan:event-case (scr event) + ((nil) nil) + (:backspace + (when (< 8 (croatoan:.cursor-position-x inputwin)) + (croatoan:move inputwin 0 -1 :relative t) + (croatoan:add-char inputwin #\space) + (croatoan:move inputwin 0 -1 :relative t) + (croatoan:refresh inputwin))) + (#\newline + (return-from croatoan:event-case + (trim-whitespace + (croatoan:extract-string inputwin + :y 3 :x 8 :n val-width)))) + (otherwise + (when (and (characterp event) + (> (+ 8 val-width) + (croatoan:.cursor-position-x inputwin))) + (croatoan:add-char inputwin event + :color-pair '(:blue :black)) + (croatoan:refresh inputwin)))))) + +(defun user-confirm-p (scr msg &optional (cls NIL)) + "Ask the user to confirm (Yes/No) a message." + (when cls (croatoan:clear scr)) + (croatoan:refresh scr) + (let ((dw (make-instance 'croatoan:dialog-window + :center t :border t :width (max 20 (+ 4 (length msg))) + :max-item-length (max 4 (halve (length msg))) + :input-blocking t :layout '(1 2) + :message-height 1 :message-text msg + :items '(" Yes" " No")))) + (croatoan:draw-menu dw) + (croatoan:event-case (scr event) + ((nil) nil) + ((:left :right) + (croatoan:update-menu dw event) + (croatoan:draw-menu dw)) + (#\newline + (return-from croatoan:event-case + (equalp (croatoan:current-item dw) " Yes")))))) + +(defun inform-user (scr msg) + "Display an informational message to the user" + ;;TODO + ) + diff --git a/src/server.lisp b/src/server.lisp index 00f9a5a..aec9fb8 100644 --- a/src/server.lisp +++ b/src/server.lisp @@ -7,12 +7,12 @@ ;;;; (c) 2018 Daniel Vedder, MIT license ;;;; -(in-package :naledi-ya-africa) - ;;XXX utility function during development, remove later (defun dt (&optional (n 0)) (bt:destroy-thread (nth n (bt:all-threads)))) +(in-package :naledi-ya-africa) + ;; TODO save and load functions ;; XXX Will probably require `make-load-form-saving-slots' @@ -50,7 +50,7 @@ (defun start-server (&optional (force T)) "Start the game server" - ;;TODO cannot restart -> ADDRESS-IN-USE ERROR + ;;FIXME cannot restart -> ADDRESS-IN-USE ERROR ;; -> comes from not closing connections properly? ;;FIXME causes Naledi to hang on `q' -> Is this really the problem? ;;XXX change force back to NIL? @@ -59,7 +59,7 @@ (reset-world-age)) (unless (or world-thread server-thread) (init-world) - (setf running T) + (setf running T) (setf world-thread (bt:make-thread #'update-loop :name "world-thread")) (setf server-thread diff --git a/src/util.lisp b/src/util.lisp index b3ceb5e..fa9cbe0 100644 --- a/src/util.lisp +++ b/src/util.lisp @@ -45,6 +45,8 @@ (defmacro while (condition &body body) "An implementation of a while loop as found in other languages" + ;;XXX There's probably an easy way to do this with `loop', but + ;; so far I've been to lazy to look... `(do () ((not ,condition) NIL) ,@body))