Files
jak-project/goal_src/jak3/engine/ui/text.gc
Jordan 427c952558 [Jak 2 and Jak 3]: Fixing menu text boundaries and drawing alignments for Automatic and Custom aspect ratios (#4380)
This PR fixes the alignment of text and other elements in several menus
of Jak 3 (as well as some of Jak 2) when using Automatic or Custom
aspect ratios. It also cleans up some of the relevant functions which
are used to draw these menus (although I'm still learning how to clean
goal code).

I mostly used french text for testing (since the french text seems to
push the font boundaries to their limits a bit more).
I used Automatic 16:9 and Custom 4:1 aspect ratios for testing to see if
my changes scale to other aspect ratios properly, comparing with PS2 4:3
for reference.



### Jak 3

### Image Color Scale

Before:
- The black/white color scale would get stretched (beyond the limits of
the menu in 4x1)
<img width="1280" height="758" alt="color-scale-16x9-broken"
src="https://github.com/user-attachments/assets/4b3b897e-1417-42da-86a1-dd78926ccb73"
/>
<img width="1283" height="747" alt="color-scale-4x1-broken"
src="https://github.com/user-attachments/assets/44d192ba-5afe-43ae-9419-021123478a31"
/>

Reference (PS2 4:3 in OpenGoal):
<img width="1278" height="773" alt="color-scale-reference"
src="https://github.com/user-attachments/assets/d9bd8f4e-8f69-49a9-8a99-3fcd3d7a4097"
/>

After fixes:

<img width="1283" height="765" alt="color-scale-16x9-fixed"
src="https://github.com/user-attachments/assets/99422d7f-d688-4d91-8ce0-6ad0b7cb10df"
/>
<img width="1282" height="762" alt="color-scale-4x1-fixed"
src="https://github.com/user-attachments/assets/6011f30a-0c31-4ea9-afe1-735ec36f3d32"
/>




### Secrets menu

Before:
- The secret titles touch their costs (#4233).
- The alignment of the top line showing orbs available/cost and total
orbs collected is broken (more noticeable in 4:1)
- The alignment of the titles for groups of secrets is broken (more
noticeable in 4:1)
- The indentation for secret titles relative to the group of secrets is
also broken (again, more noticeable in 4:1)
<img width="1286" height="758" alt="secrets-broken-16x9"
src="https://github.com/user-attachments/assets/bd6c7dab-ce95-4349-bf1d-2a09f82fcbfe"
/>
<img width="1287" height="751" alt="secrets-broken-4x1"
src="https://github.com/user-attachments/assets/3e1ce842-19ba-49b3-92f9-52034ae1982f"
/>

Reference (PS2 4:3 in OpenGoal):

<img width="1280" height="761" alt="Screenshot from 2026-08-16 22-58-22"
src="https://github.com/user-attachments/assets/7326a1c7-b479-4fb7-9450-377a05ab7963"
/>

After fixes:
<img width="1280" height="722" alt="secrets-fixed-16x9"
src="https://github.com/user-attachments/assets/0a66d45f-3aa3-4cc8-ac88-c6aef20f1319"
/>
<img width="1286" height="713" alt="secrets-fixed-4x1"
src="https://github.com/user-attachments/assets/e8740739-42ac-4a12-aad1-e6620dfc73ed"
/>



### Secrets purchase menu

Before: 

- The text for the secret you're purchasing can get cut off if too big.
- As before, the top line showing orb cost/orbs collected is misaligned.
<img width="1288" height="754" alt="secrets-purchase-broken-16x9"
src="https://github.com/user-attachments/assets/49b709e3-c015-42ba-b0aa-034769286d77"
/>
<img width="1281" height="756" alt="secrets-purchase-broken-4x1"
src="https://github.com/user-attachments/assets/0c3c5e02-048a-4188-ada7-2413c384224b"
/>

Reference (PS2 4:3 in OpenGoal):

<img width="1286" height="753" alt="secrets-purchase-reference"
src="https://github.com/user-attachments/assets/bb0b1d1a-87f6-4343-8a82-2e1f968943cd"
/>

After fixes:
<img width="1286" height="759" alt="secrets-purchase-fixed-16x9"
src="https://github.com/user-attachments/assets/1d73593f-1d58-407e-9ce6-2da5bf0fd84b"
/>
<img width="1283" height="750" alt="secrets-purchase-fixed-4x1"
src="https://github.com/user-attachments/assets/85c7e14d-c6b2-4b21-83e5-173af5cba185"
/>


### Save/Load/Create Hero Mode Save menu

Here I show the load game screen only but the bugs were the same among
all 3 as the code is shared. The fixes apply to all 3 as well 😃

Before:

- The header text is cut off in french. 
- There is a faint red rectangle to the right of the menu (look closely!
It's a bit clearer and goes beyond the menu in 4x1).
- The left yellow boxes around the save-games that are not selected do
not scale properly (more noticeable in 4x1).

<img width="1284" height="749" alt="load-broken-Automatic"
src="https://github.com/user-attachments/assets/2ae3bd8b-42ec-49bc-9851-cbaace4ce64b"
/>
<img width="1285" height="751" alt="load-broken-4x1"
src="https://github.com/user-attachments/assets/655de844-748c-43f9-a2e8-d779fe3a8e19"
/>

Reference (PS2 4:3 in OpenGoal):
<img width="1281" height="762" alt="load-screen-4x3"
src="https://github.com/user-attachments/assets/e4e320c9-999a-44a7-8b3e-34cfdbeef5a1"
/>



After Fixes:

- Now headers that exceed the width of the menu screen will be shrunken
down to fit. This will hopefully work for any other menus that need it
as well 😃
- No more red rectangle on the right, and the yellow box to the left
around the save games scales properly.

<img width="1286" height="754" alt="load-fixed-automatic"
src="https://github.com/user-attachments/assets/1a48d811-5684-4f7e-a12a-19e896e30b48"
/>
<img width="1288" height="749" alt="load-fixed-4x1"
src="https://github.com/user-attachments/assets/f5a68cb1-576a-4927-977b-b3419aaf7c23"
/>


### Missions menu 

Before:

- Mission text is sometimes cut off on the right
- Mission text is shifted to the right (more noticeable in 4x1)

<img width="1286" height="753" alt="missions-alignment-broken-16x9"
src="https://github.com/user-attachments/assets/37175eb7-e191-4c09-bb72-b40819acd99b"
/>
<img width="1281" height="750" alt="missions-alignment-broken-16x9-2"
src="https://github.com/user-attachments/assets/482f351a-7386-4211-8434-cf60c0691fdb"
/>
<img width="1286" height="756" alt="missions-broken-4x1"
src="https://github.com/user-attachments/assets/4cbfa4d0-999b-4265-bed0-957958663037"
/>
<img width="1290" height="750" alt="missions-broken-4x1-2"
src="https://github.com/user-attachments/assets/4889b498-d6aa-4e6c-87fc-4dfadeac5827"
/>

Reference (PS2 4:3 in OpenGoal):
<img width="1284" height="768" alt="missions-reference-1"
src="https://github.com/user-attachments/assets/91a526c8-08c2-4a62-8219-09e967fab9c2"
/>
<img width="1276" height="755" alt="missions-reference-2"
src="https://github.com/user-attachments/assets/65c4e944-5b1f-479a-bf7c-eca19c81f5d6"
/>


After:
<img width="1281" height="760" alt="missions-fixed-16x9"
src="https://github.com/user-attachments/assets/a972daf0-1872-46f0-a829-cd8d292df1d6"
/>
<img width="1282" height="750" alt="missions-fixed-16x9-2"
src="https://github.com/user-attachments/assets/973611d6-a72c-43f8-b2f3-034e83f6c416"
/>
<img width="1289" height="760" alt="missions-alignment-fixed-4x1"
src="https://github.com/user-attachments/assets/699c5dbc-8c39-468d-9e14-94007a789e48"
/>
<img width="1281" height="755" alt="missions-alignment-fixed-4x1-2"
src="https://github.com/user-attachments/assets/89216187-d453-4d3e-8e8b-5ae365e7227d"
/>


### Jak 2 

### Secrets menu orb icon
Position was wrong in Automatic and some (but strangely, not all) Custom
Aspect ratios. A correction for the aspect ratio was also being applied
in the setter.

Before:
<img width="1284" height="755" alt="image"
src="https://github.com/user-attachments/assets/0a15e238-1d98-4ba7-86ce-b295e8d78d7a"
/>

After:
<img width="1283" height="755" alt="image"
src="https://github.com/user-attachments/assets/bd97de44-ef41-426c-a76f-d4fe99a35285"
/>

### Missions menu
- Indentation was not working, even in 4:3, due to a typo.
- Width was broken in Automatic and Custom

Before:
<img width="1281" height="753" alt="Screenshot from 2026-08-19 01-05-54"
src="https://github.com/user-attachments/assets/6f5e3cc4-3522-45a5-a45b-34a46a7db544"
/>

After:
<img width="1287" height="761" alt="image"
src="https://github.com/user-attachments/assets/cae5498b-6ca4-40ab-a31c-feb8899bc1e7"
/>
2026-08-28 16:42:47 +02:00

645 lines
25 KiB
Common Lisp

;;-*-Lisp-*-
(in-package goal)
;; name: text.gc
;; name in dgo: text
;; dgos: GAME
;; DECOMP BEGINS
(kmemopen global "text-buffers")
(define *expand-buf-number* 0)
(define *game-text-word* (new 'global 'string 256 (the-as string #f)))
(define *game-text-line* (new 'global 'string 1024 (the-as string #f)))
(define *expanded-text-line0* (new 'global 'string 1024 (the-as string #f)))
(define *expanded-text-line1* (new 'global 'string 1024 (the-as string #f)))
(define *level-text-file-load-flag* #t)
(when (zero? (-> *common-text-heap* base))
(kheap-alloc *common-text-heap* (* 96 1024))
)
(kmemclose)
(defmethod relocate ((this game-text-info) (offset int))
(let ((v1-1 (-> *level* loading-level)))
(when v1-1
(set! (-> v1-1 loaded-text-info (-> v1-1 loaded-text-info-count)) this)
(+! (-> v1-1 loaded-text-info-count) 1)
)
)
this
)
(defmethod length ((this game-text-info))
(-> this length)
)
;; WARN: Return type mismatch uint vs int.
(defmethod asize-of ((this game-text-info))
(the-as int (+ (-> this type size) (* (-> this length) 8)))
)
(defmethod mem-usage ((this game-text-info) (usage memory-usage-block) (flags int))
(set! (-> usage length) (max 85 (-> usage length)))
(set! (-> usage data 84 name) "string")
(+! (-> usage data 84 count) 1)
(let ((v1-6 (asize-of this)))
(+! (-> usage data 84 used) v1-6)
(+! (-> usage data 84 total) (logand -16 (+ v1-6 15)))
)
(dotimes (s4-0 (-> this length))
(set! (-> usage length) (max 85 (-> usage length)))
(set! (-> usage data 84 name) "string")
(+! (-> usage data 84 count) 1)
(let ((v1-18 (asize-of (-> this data s4-0 text))))
(+! (-> usage data 84 used) v1-18)
(+! (-> usage data 84 total) (logand -16 (+ v1-18 15)))
)
)
this
)
(defun convert-korean-text ((arg0 string))
(local-vars (v1-21 int))
(let ((gp-0 (-> arg0 data)))
*expanded-text-line0*
(let ((s4-0 0))
0
(let ((s1-0 0)
(s5-0 (length arg0))
)
(set! *expand-buf-number* (logxor *expand-buf-number* 1))
(let ((s3-0 (if (zero? *expand-buf-number*)
*expanded-text-line0*
*expanded-text-line1*
)
)
)
(let ((s2-0 (+ (-> s3-0 allocated-length) -1)))
(clear s3-0)
(while (< s4-0 s5-0)
(cond
((= (-> gp-0 s4-0) 3)
(+! s4-0 1)
(while (and (< s4-0 s5-0) (!= (-> gp-0 s4-0) 3) (!= (-> gp-0 s4-0) 4))
(set! (-> s3-0 data s1-0) (-> gp-0 s4-0))
(+! s4-0 1)
(+! s1-0 1)
)
)
(else
(let ((v1-17 (+ s4-0 1)))
(-> gp-0 v1-17)
(set! s4-0 (+ v1-17 1))
)
(set! (-> s3-0 data s1-0) (the-as uint 126))
(let ((v1-19 (+ s1-0 1)))
(set! (-> s3-0 data v1-19) (the-as uint 89))
(let ((v1-20 (+ v1-19 1)))
(while (and (< s4-0 s5-0) (< v1-20 s2-0) (!= (-> gp-0 s4-0) 3) (!= (-> gp-0 s4-0) 4))
(cond
((= (-> gp-0 s4-0) 5)
(set! (-> s3-0 data v1-20) (the-as uint 1))
(+! s4-0 1)
(set! v1-21 (+ v1-20 1))
)
(else
(set! (-> s3-0 data v1-20) (the-as uint 3))
(set! v1-21 (+ v1-20 1))
)
)
(set! (-> s3-0 data v1-21) (-> gp-0 s4-0))
(+! s4-0 1)
(let ((v1-22 (+ v1-21 1)))
(set! (-> s3-0 data v1-22) (the-as uint 126))
(let ((v1-23 (+ v1-22 1)))
(set! (-> s3-0 data v1-23) (the-as uint 90))
(set! v1-20 (+ v1-23 1))
)
)
)
(set! (-> s3-0 data v1-20) (the-as uint 126))
(let ((v1-24 (+ v1-20 1)))
(set! (-> s3-0 data v1-24) (the-as uint 43))
(let ((v1-25 (+ v1-24 1)))
(set! (-> s3-0 data v1-25) (the-as uint 50))
(let ((v1-26 (+ v1-25 1)))
(set! (-> s3-0 data v1-26) (the-as uint 54))
(let ((v1-27 (+ v1-26 1)))
(set! (-> s3-0 data v1-27) (the-as uint 72))
(set! s1-0 (+ v1-27 1))
)
)
)
)
)
)
)
)
)
)
(set! (-> s3-0 data s1-0) (the-as uint 0))
s3-0
)
)
)
)
)
;; og:preserve-this extracted implementation out so that callers of `lookup-text!` were uneffected
;; this implementation is different from the original (has fallback text lookup)
(define-extern lookup-text-impl! (function game-text-info text-id symbol symbol string))
(defun lookup-text-impl! ((this game-text-info) (arg0 text-id) (arg1 symbol) (fallback-call? symbol))
(cond
((= this #f)
(cond
(arg1
(the-as string #f)
)
(else
(format (clear *temp-string*) "NO GAME TEXT")
*temp-string*
)
)
)
(else
(let* ((a1-2 0)
(a3-0 (+ (-> this length) 1))
(v1-2 (/ (+ a1-2 a3-0) 2))
)
(let ((t0-0 -1))
(while (and (!= (-> this data v1-2 id) arg0) (!= v1-2 t0-0))
(if (< (the-as uint arg0) (the-as uint (-> this data v1-2 id)))
(set! a3-0 v1-2)
(set! a1-2 v1-2)
)
(set! t0-0 v1-2)
(set! v1-2 (/ (+ a1-2 a3-0) 2))
)
)
(cond
((!= (-> this data v1-2 id) arg0)
(cond
(arg1
;; og:preserve-this Added fallback to english is string is not found.
(#if PC_PORT
(if (and *fallback-text-lookup?* (not fallback-call?))
(aif (lookup-text-impl! *fallback-text* arg0 #t #t)
it
(the-as string #f))
(the-as string #f))
(the-as string #f))
)
(else
;; og:preserve-this Added fallback to english is string is not found.
(#if PC_PORT
(if (and *fallback-text-lookup?* (not fallback-call?))
(aif (lookup-text-impl! *fallback-text* arg0 #t #t)
it
(string-format "UNKNOWN ID ~D" arg0))
(string-format "UNKNOWN ID ~D" arg0))
(string-format "UNKNOWN ID ~D" arg0))
)
)
)
((= (-> this language-id) 7)
(convert-korean-text (-> this data v1-2 text))
)
(else
(-> this data v1-2 text)
)
)
)
)
))
(defmethod lookup-text! ((this game-text-info) (arg0 text-id) (arg1 symbol))
"Look up text by ID. Will return the string.
If the ID can't be found, and arg1 is #t, it will return #f,
otherwise the temp string UNKNOWN ID <id number>"
;; og:preserve-this call custom function
(lookup-text-impl! this arg0 arg1 #f))
(defmethod lookup-text ((this level) (arg0 text-id) (arg1 symbol))
(let ((v1-0 *common-text*))
(dotimes (a3-0 (-> this loaded-text-info-count))
;; og:preserve-this added
(if (= (-> this loaded-text-info a3-0 language-id) (#if PC_PORT (the language-enum (-> *pc-settings* text-language))
(-> *setting-control* user-current language)))
(set! v1-0 (-> this loaded-text-info a3-0))
)
)
(lookup-text! v1-0 arg0 arg1)
)
)
(define text-is-loading #f)
;; WARN: Found some very strange gotos. Check result carefully, this is not well tested.
(defun load-game-text-info ((arg0 string) (arg1 (pointer object)) (arg2 kheap))
(local-vars (v0-4 int) (sv-16 object) (sv-20 int) (sv-24 int) (sv-32 int))
(set! sv-16 (-> arg1 0))
;; og:preserve-this added
(set! sv-20 (#if PC_PORT (the int (-> *pc-settings* text-language))
(-> *setting-control* user-current language)))
(set! sv-24 0)
(set! sv-32 (&- (-> arg2 top) (the-as uint (-> arg2 base))))
;; english -> UK english in PAL
;; og:preserve-this no longer necessary.
(#unless PC_PORT
(if (and (= (scf-get-territory) GAME_TERRITORY_SCEE) (= sv-24 (the int (language-enum english))) (not (demo?)))
(set! sv-24 (the int (language-enum uk-english)))
)
)
(when (and (or (= sv-16 #f)
(!= (-> (the-as game-text-info sv-16) language-id) sv-20)
(not (string= (-> (the-as game-text-info sv-16) group-name) arg0))
)
(not (load-in-progress? *level*))
)
(let ((v1-19 arg2))
(set! (-> v1-19 current) (-> v1-19 base))
)
(b! #t cfg-21 :delay (nop!))
(label cfg-20)
(set! v0-4 0)
(b! #t cfg-34 :delay (nop!))
(label cfg-21)
(let ((s3-0 str-load))
(format (clear *temp-string*) "~D~S.TXT" sv-20 arg0)
(b!
(not (s3-0
*temp-string*
-1
(logand -64 (&+ (-> arg2 current) 63))
(&- (-> arg2 top) (the-as uint (-> arg2 current)))
)
)
cfg-20
:delay (nop!)
)
)
(label cfg-23)
(let ((v1-23 (str-load-status (the-as (pointer int32) (& sv-24)))))
(cond
((= v1-23 'error)
(format 0 "Error loading text~%")
(return 0)
)
((>= sv-24 (+ sv-32 -300))
(format 0 "Game text heap overrun!~%")
(return 0)
)
((= v1-23 'busy)
(nop!)
(nop!)
(nop!)
(nop!)
(nop!)
(nop!)
(goto cfg-23)
)
)
)
(let ((s2-1 (logand -64 (&+ (-> arg2 current) 63))))
(flush-cache 0)
(let ((s3-1 link))
(format (clear *temp-string*) "~D~S.TXT" sv-20 arg0)
(set! (-> arg1 0) (s3-1 s2-1 (-> *temp-string* data) sv-24 arg2 0))
)
)
(if (<= (the-as int (-> arg1 0)) 0)
(set! (-> arg1 0) (the-as object #f))
)
)
(set! v0-4 0)
(label cfg-34)
v0-4
)
(defun load-level-text-files ((arg0 int))
;; og:preserve-this Load in English file to use as a fallback
(when (or *level-text-file-load-flag* (>= arg0 0))
(load-game-text-info "common" (&-> '*common-text* value) *common-text-heap*)
(#when PC_PORT
(protect ((-> *pc-settings* text-language))
(set! (-> *pc-settings* text-language) (pc-language english))
(load-game-text-info "common" (&-> '*fallback-text* value) *fallback-text-heap*))))
(none))
(defun draw-debug-text-box ((arg0 font-context))
(when *cheat-mode*
(let ((s5-0 (new 'static 'vector4w))
(gp-0
(new 'static 'inline-array vector4w 4
(new 'static 'vector4w)
(new 'static 'vector4w)
(new 'static 'vector4w)
(new 'static 'vector4w)
)
)
)
(set-vector! (-> gp-0 0) (the int (+ -256.0 (-> arg0 origin x))) (the int (+ -208.0 (-> arg0 origin y))) 0 1)
(set-vector!
(-> gp-0 1)
(the int (+ -256.0 (-> arg0 width) (-> arg0 origin x)))
(the int (+ -208.0 (-> arg0 origin y)))
0
1
)
(set-vector!
(-> gp-0 2)
(the int (+ -256.0 (-> arg0 width) (-> arg0 origin x)))
(the int (+ -208.0 (-> arg0 height) (-> arg0 origin y)))
0
1
)
(set-vector!
(-> gp-0 3)
(the int (+ -256.0 (-> arg0 origin x)))
(the int (+ -208.0 (-> arg0 height) (-> arg0 origin y)))
0
1
)
(set-vector! s5-0 128 128 128 128)
(add-debug-line2d #t (bucket-id debug-no-zbuf1) (-> gp-0 0) (-> gp-0 1) s5-0)
(add-debug-line2d #t (bucket-id debug-no-zbuf1) (-> gp-0 1) (-> gp-0 2) s5-0)
(add-debug-line2d #t (bucket-id debug-no-zbuf1) (-> gp-0 2) (-> gp-0 3) s5-0)
(add-debug-line2d #t (bucket-id debug-no-zbuf1) (-> gp-0 3) (-> gp-0 0) s5-0)
)
)
0
(none)
)
(defun print-game-text-scaled ((arg0 string) (arg1 float) (arg2 font-context) (arg3 bucket-id))
(let ((f26-0 (-> arg2 width))
(f30-0 (-> arg2 height))
(f24-0 (-> arg2 origin x))
(f22-0 (-> arg2 origin y))
(f28-0 (-> arg2 scale))
)
(let ((f0-1 (* (-> arg2 width) arg1))
(f1-2 (* (-> arg2 height) arg1))
)
(if (logtest? (-> arg2 flags) (font-flags middle))
(+! (-> arg2 origin x) (* 0.5 (- f26-0 f0-1)))
)
(if (logtest? (-> arg2 flags) (font-flags middle-vert))
(+! (-> arg2 origin y) (* 0.5 (- f30-0 f1-2)))
)
(set-scale! arg2 (* f28-0 arg1))
(set! (-> arg2 width) f0-1)
(set! (-> arg2 height) f1-2)
)
(print-game-text arg0 arg2 #f 44 (bucket-id hud-draw-hud-alpha))
(set! (-> arg2 origin x) f24-0)
(set! (-> arg2 origin y) f22-0)
(set! (-> arg2 width) f26-0)
(set! (-> arg2 height) f30-0)
(set! (-> arg2 scale) f28-0)
)
0
(none)
)
;; display-text : text to print
;; font-ctx : the font to draw the text with
;; invisible : will actually print the text if true, otherwise will not print
;; arg3: height of text if text is supposed to be large
;; bucket-id: buffer used to draw?
;; Returns the height of the printed text.
(defun print-game-text ((display-text string) (font-ctx font-context) (invisible symbol) (large-font-size int) (bucket-id bucket-id))
(local-vars
(font-origin-x float) ;; Initial x and y position of font context
(font-origin-y float)
(font-flags font-flags)
(font-color font-color)
(cur-ch-ptr (pointer uint8)) ;; Char pointer used to traverse chars of display-text
(font-cur-position float)
(font-origin-x-copy float)
(right-side float) ;; Right boundary of where there is to draw
(bottom-side float) ;; Bottom boundary of where there is to draw
(whitespace-width float) ;; The width of a whitespace char using font-ctx
(line-height float) ;; The height of a line printed with font-ctx
(non-empty-lines int) ;; The number of lines printed so far
(cur-ch uint) ;; Current character inside display-text
(game-word-len int) ;;The current length of game-text-word
(game-line-len int) ;;The current length of game-text-line
(num-lines int)
(next-ch uint) ;;The next character inside display-text
(word-break symbol) ;; True if and only if game-text-word is complete and should be put in game-text-line
(num-lines-to-print int) ;;The number of outstanding lines that still need to be printed.
(game-text-word-width int)
)
(cond
((< 0.1 (-> font-ctx scale)) ;; Only print if font's scale is greater than 0.1
;;Storing font-ctx properties to restore them later.
(set! font-origin-x (-> font-ctx origin x))
(set! font-origin-y (-> font-ctx origin y))
(set! font-flags (-> font-ctx flags))
(set! font-color (-> font-ctx color))
(set-context! *font-work* font-ctx)
(set! (-> font-ctx max-x) (-> font-ctx origin x))
(when (logtest? (-> font-ctx flags) (font-flags middle-vert)) ;; If middle-vert flag is set,
(logclear! (-> font-ctx flags) (font-flags middle-vert)) ;; Clear the flag
(+! (-> font-ctx origin y) ;; font's y-origin += 0.5 * (font-height - height needed to print display-text)
(the float
(the int (* 0.5 (- (-> font-ctx height) (print-game-text display-text font-ctx #t 44 (bucket-id hud-draw-hud-alpha)))))
)
)
)
(set! cur-ch-ptr (-> display-text data))
(set! font-cur-position (-> font-ctx origin x))
(set! font-origin-x-copy (-> font-ctx origin x))
(set! right-side (+ (-> font-ctx origin x) (-> font-ctx width)))
(set! bottom-side (+ (-> font-ctx origin y) (-> font-ctx height)))
(set! whitespace-width (-> (get-string-length " " font-ctx) length))
;;line-height is set to font-ctx scale * (large-font-size if font-ctx has its large flat set or 28.0)
(set! line-height (* (-> font-ctx scale)
(if (logtest? (-> font-ctx flags) (font-flags large))
(the float large-font-size)
28.0
)
)
)
(set! non-empty-lines 0)
(if (logtest? (-> font-ctx flags) (font-flags middle))
(+! (-> font-ctx origin x) (/ (-> font-ctx width) 2))
)
(set! cur-ch (-> cur-ch-ptr 0))
(set! game-word-len 0)
(set! game-line-len 0)
(set! num-lines 0)
(set! next-ch (-> cur-ch-ptr 1)) ;;Following character in display-text
(set! word-break (the-as symbol #f)) ;;Set to false
(set! num-lines-to-print 0)
(set! game-text-word-width 0)
(set! (-> *game-text-line* data 0) (the-as uint 0)) ;;zero
;; While there are more characters to read from display-text (cur-ch is not \0) into game-text-word,
;; game-text-word hasn't been written to game-text-line or game-text-line hasn't been printed
;; and the font context's vertical position isn't below the bottom of the drawing context.
(while (and (not (and (zero? cur-ch) (zero? game-word-len) (zero? game-line-len))) (>= bottom-side (-> font-ctx origin y)))
(set! game-text-word-width 0)
;; (set! cur-ch-ptr (&-> cur-ch-ptr 1))
;; (set! next-ch (-> cur-ch-ptr 0))
;; (set! cur-ch-ptr (&-> cur-ch-ptr -1))
(set! next-ch (-> cur-ch-ptr 1))
(cond
;; 0 < current character < 4
((and (> cur-ch 0) (< cur-ch (the-as uint 4)))
;; Copy current and next character from display text into game-text-word
(set! (-> *game-text-word* data game-word-len) cur-ch)
(set! game-word-len (+ game-word-len 1))
(set! (-> *game-text-word* data game-word-len) next-ch)
(set! game-word-len (+ game-word-len 1))
(set! cur-ch-ptr (&-> cur-ch-ptr 1)) ;; Increment pointer within display-text
)
;; Current character is a space or forward slash, or it's a minus and previous was a tilde.
;; Might need to check that cur-ch-ptr - 1 is not out of bounds?
((or (= cur-ch #\\s) (= cur-ch #\/) (and (= cur-ch #\-) (!= (-> cur-ch-ptr -1) #\~)))
(set! (-> *game-text-word* data game-word-len) cur-ch) ;;Write the character into the word
(set! game-word-len (+ game-word-len 1))
(set! word-break #t) ;; Should copy current game-text-word into game-text-line
)
;; Current character is 0
((zero? cur-ch)
(if (nonzero? game-word-len) ;;If game-text-word not empty
(set! word-break #t) ;; Should copy current game-text-word into game-text-line
)
(set! num-lines-to-print (+ num-lines-to-print 1))
)
;; Both current character and next are backslashes
((and (= cur-ch #\\) (= next-ch #\\))
(set! cur-ch-ptr (&-> cur-ch-ptr 1)) ;; Increment pointer within display-text
(if (nonzero? game-word-len)
(set! word-break #t) ;; Should copy current game-text-word into game-text-line
)
(set! num-lines-to-print (+ num-lines-to-print 1))
)
;; Current character and next are underscores
((and (= cur-ch #\_) (= next-ch #\_))
(set! cur-ch-ptr (&-> cur-ch-ptr 1)) ;; Increment pointer within display-text
(set! (-> *game-text-word* data game-word-len) #\\s) ;;Write a space into the word
(set! game-word-len (+ game-word-len 1))
)
(else ;;Write character into word and increment word length
(set! (-> *game-text-word* data game-word-len) cur-ch)
(set! game-word-len (+ game-word-len 1))
)
)
(when word-break
(set! (-> *game-text-word* data game-word-len) (the-as uint 0)) ;;Null terminate word
(let ((word-start font-cur-position))
;;Set game-text-word-width to the length of game-text-word using font-ctx
(set! game-text-word-width (the int (-> (get-string-length *game-text-word* font-ctx) length)))
(let ((word-end (+ word-start (the float game-text-word-width))))
(if (= (-> *game-text-word* data (+ game-word-len -1)) #\\s) ;; If last character is a space
(set! word-end (- word-end whitespace-width))
)
(cond
((< right-side word-end) ;; If word would exceed right side of screen,
(set! (-> font-ctx max-x) (fmax (-> font-ctx max-x) font-cur-position))
(set! num-lines-to-print (+ num-lines-to-print 1))
)
(else
;;Append game-word to the end of game-line
(copy-charp<-charp (&-> *game-text-line* data game-line-len) (-> *game-text-word* data))
(set! game-line-len (+ game-line-len game-word-len))
(set! game-word-len 0)
(set! font-cur-position (+ font-cur-position (the float game-text-word-width))) ;; Add the word width to font-cur-position
(set! (-> font-ctx max-x) (fmax (-> font-ctx max-x) font-cur-position))
(set! word-break (the-as symbol #f))
)
)
)
)
)
(while (> num-lines-to-print 0)
;; Store the position of the current line + line-height
(let ((next-line-origin-y (+ (-> font-ctx origin y) line-height)))
;;Note: for now it seems like font-ctx start-line is always 0.
(when (>= num-lines (the-as int (-> font-ctx start-line)))
;; If game-text-line ends in a space, replace the space with 0
;; (should probably decrement length too??)
(when (= (-> *game-text-line* data (+ game-line-len -1)) #\\s)
(set! (-> *game-text-line* data (+ game-line-len -1)) (the-as uint 0))
0
)
;;Actually print the text onscreen if the text is not meant to be invisible.
(when (not invisible)
(with-dma-buffer-add-bucket ((s2-1 (-> *display* frames (-> *display* on-screen) global-buf))
bucket-id
)
(draw-string *game-text-line* s2-1 font-ctx)
)
)
(if (nonzero? (-> *game-text-line* data 0))
(set! non-empty-lines (+ non-empty-lines 1)) ;; Increment non-empty lines "printed"
)
;; Update the font-context to be positioned after the previous line
(set! (-> font-ctx origin y) next-line-origin-y)
)
)
(set! num-lines (+ num-lines 1))
(set! (-> *game-text-line* data 0) (the-as uint 0))
(set! game-line-len 0)
(set! num-lines-to-print (+ num-lines-to-print -1))
(set! font-cur-position font-origin-x-copy)
(when word-break
;;Copy game-text-word to the end of game-text-line
(copy-charp<-charp (&-> *game-text-line* data game-line-len) (-> *game-text-word* data))
(set! game-line-len (+ game-line-len game-word-len))
(set! game-word-len 0)
(set! word-break (the-as symbol #f)) ;; Reset
(set! font-cur-position (+ font-cur-position (the float game-text-word-width))) ;; Add word width to current position
)
)
(when (nonzero? cur-ch)
(set! cur-ch-ptr (&-> cur-ch-ptr 1))
(set! cur-ch (-> cur-ch-ptr 0))
)
)
;; Restore font-ctx properties.
(set! (-> font-ctx origin x) font-origin-x)
(set! (-> font-ctx origin y) font-origin-y)
(set! (-> font-ctx flags) font-flags)
(set! (-> font-ctx color) font-color)
;; og:preserve-this
(#when PC_PORT
(if (and *debug-segment* *display-text-box*)
(draw-debug-text-box font-ctx)))
;;
(if (> non-empty-lines 0)
(* line-height (the float non-empty-lines))
0.0
)
)
(else
0.0
)
)
)
(defun disable-level-text-file-loading ()
(set! *level-text-file-load-flag* #f)
0
(none)
)
(defun enable-level-text-file-loading ()
(set! *level-text-file-load-flag* #t)
0
(none)
)