From 1d30f66df4ed37f47ddeb26931432e8aeff21448 Mon Sep 17 00:00:00 2001 From: Jaidyn Ann <10477760+JadedCtrl@users.noreply.github.com> Date: Sun, 2 Jul 2023 11:54:16 -0500 Subject: [PATCH] Fix dialogue linebreaks; surround text with spaces MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit … finally! It took so dang long to get linebreaking and positioning working in a satisfactory way, but it’s done, dammit! And no-one can break it, never again! Also, surrounding the text with spaces makes it a bit more legible. Nice touch, right? =w= --- dialogue.lisp | 99 ++++++++++++++++++++++++++++++--------------------- display.lisp | 2 +- util.lisp | 8 ++--- 3 files changed, 64 insertions(+), 45 deletions(-) diff --git a/dialogue.lisp b/dialogue.lisp index 255c747..f5f649f 100644 --- a/dialogue.lisp +++ b/dialogue.lisp @@ -220,8 +220,8 @@ Returns the state for use with STATE-LOOP, pay attention!" 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." +Otherwise, return a list list with the coordinates, textbox width, and textbox +height — all parameters for use with RENDER-STRING & co." (let* ((text-x-margin (if rightp (+ (getf coords :x) 3) 0)) @@ -231,30 +231,35 @@ use with RENDER-STRING." (- (getf coords :x) 3)))) (lines (ignore-errors (str:lines (…:linewrap-string text text-width)))) (text-height (length lines))) - (format *error-output* "HEIGHT: ~A WIDTH ~A LINES: ~A~%" text-height text-width lines) + (format *error-output* "HORIZ COORD: ~A HEIGHT: ~A WIDTH ~A LINES:~%~S~%" coords text-height text-width lines) ;; When this layout is valid… (when (and lines (>= height text-height) ;; If the text’ll fit on screen (> text-width 10)) ;; If the text is wide enough to be legible - (let ((y (…:at-least 0 (- (getf coords :y) - (if (eq (length lines) 1) ;; Align toward the speaker’s face - 1 0) - (floor (/ (length lines) 2))))) - (x (if (and (not rightp) - (eq (length lines) 1)) - (- text-width (length text)) - text-x-margin))) - (list (list :x x :y y) ;; Coords - text-width ;;(+ x text-width) ;; Max column - height))))) ;; Max row + (let* ((y (…:at-least 0 (- (getf coords :y) + (if (eq text-height 1) ;; Align toward the speaker’s face + 1 0) + (floor (/ text-height 2))))) + (x (if (and (not rightp) + (eq (length lines) 1)) + (- text-width (length text)) + text-x-margin)) + (y-margin (if (> (+ y (length lines)) height) ;; How many lines are off-screen + (- (+ y (length lines)) height) + 0))) + (list + ;; Coords of text-box’es top-left corner + (list :x x :y (- y y-margin)) + text-width ;; Width of text-box + text-height))))) ;; Height of text-box (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)." +is found, otherwise return a list of the coordinates, textbox width, and textbox +height (for use as parameters with RENDER-STRING et al.)." (let* ((text-y-margin (if downp (+ (getf coords :y) 2) (- (getf coords :y) 2))) @@ -263,23 +268,28 @@ is found, otherwise return a list of the coordinates, max-column, and max-row (- text-y-margin 1))) (text-width (floor (* width 3/5))) ;; Too wide’s illegible! So ⅗-screen. (lines (ignore-errors (str:lines (…:linewrap-string text text-width))))) - (format *error-output* "HEIGHT: ~A WIDTH ~A LINES: ~A~%" text-height text-width lines) + (format *error-output* "VERT HEIGHT: ~A WIDTH ~A LINES: ~A~%" text-height text-width lines) ;; When the text can be printed with this layout… (when (and lines (>= text-height (length lines))) - (let ((y (…:at-least - 0 - (if downp - text-y-margin - (- text-y-margin (length lines))))) - (x (…:at-least - 0 - (- (getf coords :x) - (if (eq (length lines) 1) - (floor (/ (length (car lines)) 2)) - (floor (/ text-width 2))))))) - (list (list :x x :y y) ;; Coords - text-width ;;(+ x text-width) ;; Max column - (+ y text-height)))))) ;; Max row + (let* ((y (…:at-least + 0 + (if downp + text-y-margin + (- text-y-margin (length lines))))) + (x (…:at-least + 0 + (- (getf coords :x) + (if (eq (length lines) 1) + (floor (/ (length (car lines)) 2)) + (floor (/ text-width 2)))))) + (x-margin (if (> (+ x text-width) width) + (- (+ x text-width) width) + 0))) + (list + ;; Coords of text-box’es top-left corner + (list :x (- x x-margin) :y y) + text-width ;; Width of the text-box + (length lines)))))) ;; Height of text-box (defun optimal-speech-layout (map dialogue &key (width 72) (height 20)) @@ -305,22 +315,30 @@ and max-row; for use with RENDER-STRING. Like so: (optimal-text-placement-vertically text coords :width width :height height :downp (not playerp))))) +(defun ensure-dialogue-layout (map dialogue-list) + "Given a DIALOGUE-LIST, ensure that the FIRST line of dialogue has a :layout +property — that is, a property detailing the optimal width and coordinates for +its display." + (when (and (getf (car dialogue-list) :text) + (not (getf (car dialogue-list) :layout))) + (setf (getf (car dialogue-list) :layout) + (optimal-speech-layout map (car dialogue-list))))) -(defun render-dialogue-block (matrix map dialogue) + +(defun render-dialogue-block (matrix 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)) (text (getf dialogue :text)) - (optimal-layout (when text (optimal-speech-layout map dialogue))) + (optimal-layout (getf dialogue :layout)) (coords (car optimal-layout))) (when (and text optimal-layout) - (format *error-output* "~A~%" optimal-layout) -;; (✎:render-fill-rectangle matrix #\space -;; (list :x (- (getf coords :x) 1) -;; :y (- (getf coords :y) 1) -;; (- (second optimal-layout) (getf coords :x) -2) -;; (- (third optimal-layout) (getf coords :y) -2)) + (✎:render-fill-rectangle matrix #\space + (list :x (- (getf coords :x) 1) + :y (- (getf coords :y) 1)) + (+ (second optimal-layout) 2) ;; Width + (+ (third optimal-layout) 1)) ;; Height (✎:render-string matrix text (first optimal-layout) :width (second optimal-layout) @@ -332,7 +350,8 @@ make it pretty, dang it! >O< Helper function for DIALOGUE-STATE." (when (getf (car dialogue-list) :text) (✎:show-cursor) - (render-dialogue-block matrix map (car dialogue-list)))) + (ensure-dialogue-layout map dialogue-list) + (render-dialogue-block matrix (car dialogue-list)))) diff --git a/display.lisp b/display.lisp index 8de2b9b..747de8c 100644 --- a/display.lisp +++ b/display.lisp @@ -102,7 +102,7 @@ No word-wrapping is done, even if the line exceeds the MATRIX’es size!" (y (getf coords :y))) (render-string-verbatim matrix - (…:linewrap-string (subseq text 0 char-count) width) + (subseq (…:linewrap-string text width) 0 char-count) coords))) diff --git a/util.lisp b/util.lisp index 67580eb..81abfa5 100644 --- a/util.lisp +++ b/util.lisp @@ -61,8 +61,9 @@ side (either :CENTER, :LEFT, or :RIGHT)." (defun linewrap-string (string width) "Break a STRING into several lines, each one no larger than WIDTH. Uses newlines and hypens (to break long words) as necessary." - (let ((spaces (append '(0) (search-all " " string))) - (index width)) + (let* ((string (str:replace-all (string #\newline) " " string)) + (spaces (append '(0) (search-all " " string))) + (index width)) (loop while (< index (length string)) do (let ((closest-space (car (closest-below index spaces))) (old-index (- index width))) @@ -71,8 +72,7 @@ newlines and hypens (to break long words) as necessary." ;; Break up long words with a hyphen (return (linewrap-string - (str:insert "- " (- index 1) - (str:replace-all (string #\newline) " " string)) + (str:insert "- " (- index 1) string) width)) ;; Replace eligible spaces with newlines uwu (progn