Legiblify :š¬; finish dialogue-printing with ENTER
Like with RPGs!
This commit is contained in:
parent
fe9b74a600
commit
666c155f95
|
@ -28,7 +28,23 @@
|
||||||
|
|
||||||
|
|
||||||
;;; āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
|
;;; āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
|
||||||
;;; Dialogue-generation DSL (sorta)
|
;;; Misc. utilities
|
||||||
|
;;; āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
|
||||||
|
(defun pressed-enter-p ()
|
||||||
|
"Whether or not the enter/return key has been pressed recently.
|
||||||
|
Man, todayās a good day. Well, it wasnāt great, too be honest. Kind of bad,
|
||||||
|
I slightly humiliated myself a tiny bit. But wow, Iām having such nice tea!
|
||||||
|
Programming with nice tea! What a nice day this is. If you happen to be
|
||||||
|
reading this, I hope your day is going well too!
|
||||||
|
If not, have some tea on me: Iām paying. =w="
|
||||||
|
(and (listen)
|
||||||
|
(eq (getf (āØ:normalize-char-plist (āØ:read-char-plist)) :char)
|
||||||
|
#\return)))
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
;;; āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
|
||||||
|
;;; Dialogue-generation helpers
|
||||||
;;; āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
|
;;; āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
|
||||||
(defun start-dialogue (&rest dialogue-tree)
|
(defun start-dialogue (&rest dialogue-tree)
|
||||||
(reduce (lambda (a b) (append a b))
|
(reduce (lambda (a b) (append a b))
|
||||||
|
@ -56,6 +72,8 @@
|
||||||
;;; Accessors
|
;;; Accessors
|
||||||
;;; āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
|
;;; āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
|
||||||
(defun dialogue-speaker (dialogue)
|
(defun dialogue-speaker (dialogue)
|
||||||
|
"Get the DIALOGUE-speakerās corresponding identifying symbol.
|
||||||
|
Because theyāre stored in strings. So we gotta, like, unstringify. Ya dig?"
|
||||||
(intern (string-upcase (getf dialogue :speaker))))
|
(intern (string-upcase (getf dialogue :speaker))))
|
||||||
|
|
||||||
|
|
||||||
|
@ -63,13 +81,6 @@
|
||||||
;;; āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
|
;;; āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
|
||||||
;;; Dialogue logic
|
;;; Dialogue logic
|
||||||
;;; āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
|
;;; āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
|
||||||
(defun pressed-enter-p ()
|
|
||||||
"Whether or not the enter/return key has been pressed recently."
|
|
||||||
(and (listen)
|
|
||||||
(eq (getf (āØ:normalize-char-plist (āØ:read-char-plist)) :char)
|
|
||||||
#\return)))
|
|
||||||
|
|
||||||
|
|
||||||
(defun appropriate-face (map speaker face)
|
(defun appropriate-face (map speaker face)
|
||||||
"Return the face appropriate for the speaker.
|
"Return the face appropriate for the speaker.
|
||||||
If FACE is a string, used that.
|
If FACE is a string, used that.
|
||||||
|
@ -110,19 +121,27 @@ should be printed on the screen at any given moment."
|
||||||
|
|
||||||
|
|
||||||
(defun dialogue-state-update (map dialogue-list)
|
(defun dialogue-state-update (map dialogue-list)
|
||||||
"The logic/input-processing helper function for DIALOGUE-STATE."
|
"The logic/input-processing helper function for DIALOGUE-STATE.
|
||||||
|
Progress through the lines of dialogue when the user hits ENTER, etc.
|
||||||
|
Returns the state for use with STATE-LOOP, pay attention!"
|
||||||
(update-speaking-face map (car dialogue-list))
|
(update-speaking-face map (car dialogue-list))
|
||||||
(progress-line-delivery (car dialogue-list))
|
(progress-line-delivery (car dialogue-list))
|
||||||
;; Progress to the next line of dialogue as appropriate.
|
;; Progress to the next line of dialogue as appropriate.
|
||||||
(let ((text (getf (car dialogue-list) :text)))
|
(let* ((text (getf (car dialogue-list) :text))
|
||||||
(cond ((or (pressed-enter-p)
|
(finished-printing-p (eq (length text)
|
||||||
(not text))
|
(getf (car dialogue-list) :progress)))
|
||||||
|
(did-press-enter-p (pressed-enter-p)))
|
||||||
|
(cond ((or (not text)
|
||||||
|
(and did-press-enter-p finished-printing-p))
|
||||||
(if (cdr dialogue-list)
|
(if (cdr dialogue-list)
|
||||||
(list :dialogue (cdr dialogue-list) :map map)
|
(list :dialogue (cdr dialogue-list) :map map)
|
||||||
(progn
|
(progn
|
||||||
(ā:hide-cursor)
|
(ā:hide-cursor)
|
||||||
(values nil
|
(values nil
|
||||||
(list :map map)))))
|
(list :map map)))))
|
||||||
|
((and did-press-enter-p (not finished-printing-p))
|
||||||
|
(setf (getf (car dialogue-list) :progress) (length text))
|
||||||
|
(list :dialogue dialogue-list :map map))
|
||||||
((cdr dialogue-list)
|
((cdr dialogue-list)
|
||||||
(list :dialogue dialogue-list :map map)))))
|
(list :dialogue dialogue-list :map map)))))
|
||||||
|
|
||||||
|
@ -131,7 +150,13 @@ should be printed on the screen at any given moment."
|
||||||
;;; āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
|
;;; āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
|
||||||
;;; Dialogue drawing
|
;;; Dialogue drawing
|
||||||
;;; āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
|
;;; āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
|
||||||
(defun optimal-speech-layout-horizontally (text coords &key (rightp nil) (width 72) (height 20))
|
(defun optimal-text-placement-horizontally (text coords &key (rightp nil) (width 72) (height 20))
|
||||||
|
"Given a horizontal direction (RIGHTP defined or nil) and a focal point COORDS,
|
||||||
|
return the parameters of a text-box that can optimally fit the given TEXT in the
|
||||||
|
direction specified relative to the focal point. If a legible position canāt be
|
||||||
|
found, just give up! Return nil.
|
||||||
|
Otherwise, return a list list with the coordinates, max column, and max row ā for
|
||||||
|
use with RENDER-STRING."
|
||||||
(let* ((text-x-margin (if rightp
|
(let* ((text-x-margin (if rightp
|
||||||
(+ (getf coords :x) 3)
|
(+ (getf coords :x) 3)
|
||||||
0))
|
0))
|
||||||
|
@ -158,7 +183,12 @@ should be printed on the screen at any given moment."
|
||||||
height))))) ;; Max row
|
height))))) ;; Max row
|
||||||
|
|
||||||
|
|
||||||
(defun optimal-speech-layout-vertical (text coords &key (downp nil) (width 72) (height 20))
|
(defun optimal-text-placement-vertically (text coords &key (downp nil) (width 72) (height 20))
|
||||||
|
"Given a vertical direction (DOWNP defined or nil) and a focal point COORDS,)
|
||||||
|
return the parameters of a text-box that can optimally fit the given TEXT in the
|
||||||
|
direction specified relative to the focal point. Return nil if no such placement
|
||||||
|
is found, otherwise return a list of the coordinates, max-column, and max-row
|
||||||
|
(for use with RENDER-STRING)."
|
||||||
(let* ((text-y-margin (if downp
|
(let* ((text-y-margin (if downp
|
||||||
(+ (getf coords :y) 1)
|
(+ (getf coords :y) 1)
|
||||||
(- (getf coords :y) 2)))
|
(- (getf coords :y) 2)))
|
||||||
|
@ -186,25 +216,34 @@ should be printed on the screen at any given moment."
|
||||||
|
|
||||||
|
|
||||||
(defun optimal-speech-layout (map dialogue &key (width 72) (height 20))
|
(defun optimal-speech-layout (map dialogue &key (width 72) (height 20))
|
||||||
|
"Given a line of DIALOGUE and MAP data, return the ideal ātext-boxā for the
|
||||||
|
text. This tries to place the text on the screen without covering up anything
|
||||||
|
important, if possible.
|
||||||
|
The data returned is a list of the boxāes top-left coordinate, max-column,
|
||||||
|
and max-row; for use with RENDER-STRING. Like so:
|
||||||
|
((:x X :y Y) MAX-COLUMN MAX-ROW)"
|
||||||
(let* ((speaker-id (dialogue-speaker dialogue))
|
(let* ((speaker-id (dialogue-speaker dialogue))
|
||||||
(direction (š:getf-entity-data map speaker-id :direction))
|
(direction (š:getf-entity-data map speaker-id :direction))
|
||||||
(playerp (eq speaker-id 'player))
|
(playerp (eq speaker-id 'player))
|
||||||
(leftp (not (eq direction 'š:right)))
|
(leftp (not (eq direction 'š:right)))
|
||||||
(text (getf dialogue :text))
|
(text (getf dialogue :text))
|
||||||
(coords (š:world-coords->screen-coords (š:getf-entity-data map speaker-id :coords))))
|
(coords (š:world-coords->screen-coords (š:getf-entity-data map speaker-id :coords))))
|
||||||
(format *error-output* "AAA ~A - ~A - ~A" leftp direction 'RIGHT)
|
;; Ideally, place text-box behind the speaker; otherwise, place it above (NPC) or below (player).
|
||||||
(or (optimal-speech-layout-horizontally text coords :width width :height height
|
(or (optimal-text-placement-horizontally text coords :width width :height height
|
||||||
:rightp leftp)
|
:rightp leftp)
|
||||||
(optimal-speech-layout-vertical text coords :width width :height height
|
(optimal-text-placement-vertically text coords :width width :height height
|
||||||
:downp playerp)
|
:downp playerp)
|
||||||
(optimal-speech-layout-horizontally text coords :width width :height height
|
;; ā¦ Worst-case scenario, just do whateverāll fit :w:ā
|
||||||
:rightp (not leftp))
|
(optimal-text-placement-vertically text coords :width width :height height
|
||||||
(optimal-speech-layout-vertical text coords :width width :height height
|
:downp (not playerp))
|
||||||
:downp (not playerp)))))
|
(optimal-text-palcement-horizontally text coords :width width :height height
|
||||||
|
:rightp (not leftp)))))
|
||||||
|
|
||||||
|
|
||||||
(defun render-dialogue-block (matrix map dialogue)
|
(defun render-dialogue-block (matrix map dialogue)
|
||||||
|
"Render a bit of DIALOGUE to the MATRIX, in an intelligent fashion; that is,
|
||||||
|
make it pretty, dang it! >O<
|
||||||
|
ā:.ļ½”.o(ā§ā½ā¦)o.ļ½”.:ā"
|
||||||
(let* ((progress (getf dialogue :progress))
|
(let* ((progress (getf dialogue :progress))
|
||||||
(text (getf dialogue :text))
|
(text (getf dialogue :text))
|
||||||
(optimal-layout (when text (optimal-speech-layout map dialogue))))
|
(optimal-layout (when text (optimal-speech-layout map dialogue))))
|
||||||
|
@ -229,13 +268,13 @@ Helper function for DIALOGUE-STATE."
|
||||||
;;; Dialogue loop
|
;;; Dialogue loop
|
||||||
;;; āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
|
;;; āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
|
||||||
(defun dialogue-state (matrix &key dialogue map)
|
(defun dialogue-state (matrix &key dialogue map)
|
||||||
"Render a bit of dialogue to the screen, using :FLORA-SEARCH-AURORA.OVERWORLD
|
"Render a bit of DIALOGUE to the screen, using :FLORA-SEARCH-AURORA.OVERWORLD
|
||||||
entities as the speakers. Dialogue should be in the format:
|
entities as the speakers. Dialogue should be in the format:
|
||||||
((:text \"Hello, papa!\"
|
((:text \"Hello, papa!\"
|
||||||
:speaker \"son\" ;; The entityās ID (if applicable)
|
:speaker \"son\" ;; The entityās ID (if applicable)
|
||||||
:face \"owo\") ;; If you want their face to change
|
:face \"owo\") ;; If you want their face to change
|
||||||
(:face \"=w=\" :speaker 'son) ;; change their face back when done talking
|
(:face \"=w=\" :speaker 'son) ;; change their face back when done talking
|
||||||
(:text \"Hello, you little gremlin! <3\"
|
(:text \"My dearest son, itās been so long~!\"
|
||||||
:speaker \"papa\"
|
:speaker \"papa\"
|
||||||
...))
|
...))
|
||||||
A state-function for use with STATE-LOOP."
|
A state-function for use with STATE-LOOP."
|
||||||
|
|
|
@ -24,6 +24,7 @@
|
||||||
(:export #:overworld-state #:overworld-state-draw
|
(:export #:overworld-state #:overworld-state-draw
|
||||||
#:world-coords->screen-coords
|
#:world-coords->screen-coords
|
||||||
#:getf-entity #:getf-entity-data
|
#:getf-entity #:getf-entity-data
|
||||||
|
:left :right
|
||||||
:player))
|
:player))
|
||||||
|
|
||||||
(in-package :flora-search-aurora.overworld)
|
(in-package :flora-search-aurora.overworld)
|
||||||
|
|
ÅarÄanteā¦
Reference in New Issue