Logo Pending


A Short Tangent – KQ6 Drop Caps

How does King’s Quest VI do that thing where if the Narrator says something, it includes a fancy drop cap? Well, it’s sorta similar to how KQ5 did, but implemented better. Mostly because it’s a newer game and not basically the first on a new engine.

Requirements: a view full of letters, all the same size, and a font where the tab character is as wide as one of these letters.

First, some technical backstory. Narrator is a special class with methods to speak a specific message. It will decide on its own if it should display the passed message in a window of some kind, or play it back as spoken audio. It will also handle timing, disposing of the window and everything at the end. The messages themselves are provided by the Messager class.

A Talker is a kind of Narrator that builds on the above to add talking heads. Fortunately we won’t have to go any deeper into those because this post is all about KQ6’s drop caps, and it’s only Narrator who uses those.

Specifically, KQ6 uses a special DropCapNarrator, in fact. It’s just like a regular Narrator, but overrides the display method to add some extra bits:

(class DropCapNarrator of Narrator
  (properties
    ; Narrator properties elided.
    strView 945 ; the view with the letters in
  )
 
  (method (display theText &tmp theTalkWidth newWindow theFirst theDropCap)
    ; Ensure we don't clip the screen edge.
    (if (> (+ x talkWidth) 318)
      (= theTalkWidth (- 318 x))
    else
      (= theTalkWidth talkWidth)
    )
 
    ; Clone up a new copy of one of those fancy woody windows.
    ((= newWindow (gWindow new:))
      color: color
      back: back
    )
    ; That is, set newWindow's colors to our own.
 
    ; If we have mouse support and we're not using the invisible
    ; cursor, remember what we -do- use and go invisible.
    (if (and (not (HaveMouse)) (!= gCursorNumber 996))
      (= saveCursor gCursorNumber)
      (gGame setCursor: 996)
    else
      (= saveCursor null)
    )
 
    ; Now we're ready for the magic.
 
    ; Grab the first letter of the message text we're to show.
    (= theFirst (StrAt theText 0))
    (if (and (>= 90 theFirst) (>= theFirst 65))
      ; The first character is between 'A' and 'Z' inclusive.
      ; Replace that first character with a tab.
      (StrAt theText 0 9)
      ; Remember, the tab is as wide as a drop cap!
 
      ; Create a new DIcon for our cap and set it up.
      ((= theDropCap (DIcon new:))
        view: strView
        ; Loop 0 is A to M, loop 1 is N to Z.
        loop: (+ 0 (/ (- theFirst 65) 13))
        cel: (mod (- theFirst 65) 13)
      )
 
      ; Now display our message on screen...
      (Print
        window: newWindow
        posn: x y
        font: font
        width: theTalkWidth
        title: (if showTitle name else null)
        ; ...but with the text shifted down a little...
        addText: theText 0 7
        ; ...and that DIcon on top of it.
        addIcon: newDIcon 0 0 0 0
        modeless: true
        init:
      )
    else
      ; Not a valid drop cap so we can skip all that.
      (Print
        window: newWindow
        posn: x y
        font: font
        width: theTalkWidth
        title: (if showTitle name else null)
        ; See? No positioning or DIcon here.
        addText: theText
        modeless: true
        init:
      )
    )
    ; The rest is up to the regular Narrator. 
  )  
)

Personally, I’d set up most of those properties, then stop and consider if we have a drop cap. Duplicating all those sends… it’s still better than what KQ5 did.

Like
[ , , ]

2 thoughts on “A Short Tangent – KQ6 Drop Caps

Leave a Reply

Your email address will not be published. Required fields are marked *